Skip to content

Commit

Permalink
Update documentation for new DOMUser API #73
Browse files Browse the repository at this point in the history
  • Loading branch information
Andre Medeiros committed Feb 11, 2015
1 parent 940d81c commit 1641a7f
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 34 deletions.
49 changes: 24 additions & 25 deletions rx-run/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ DOM Rendering.
is a hard dependency, which simplifies all code related to events, asynchrony, and
errors. Structuring the app with RxJS also separates concerns, because Rx decouples
data production from data consumption. As result, apps in Cycle have nothing comparable
to imperative calls such as `setState()`, `forceUpdate()`, `replaceProps()`,
to imperative calls such as `setState()`, `forceUpdate()`, `replaceProps()`,
`handleClick()`, etc.
* **Unidirectional Dataflow**: based on the Model-View-Intent architecture, data moves
from Model to View, events move from View to Intent, and Intent emits "user intentions"
to the Model. Model handles information, View handles display, Intent handles interaction.
They are tied together as a circular loop, each one reacting to the other, but none is
They are tied together as a circular loop, each one reacting to the other, but none is
controlling the others.
* **Functions, not classes**: each node in the MVI cycle behaves like a function,
receiving events as input, and outputting events. No side effects. This makes it
Expand All @@ -24,7 +24,7 @@ DOM Rendering.
The use of [virtual-dom](https://github.com/Matt-Esch/virtual-dom) keeps performance
fast by patching the actual DOM with only the minimum necessary changes.
* **Work in progress**: API design is the current priority and might significantly evolve,
performance and other issues are left aside before this gets stable.
performance and other issues are left aside before this gets stable.

[![npm version](https://badge.fury.io/js/cyclejs.svg)](http://badge.fury.io/js/cyclejs)
[![Bower version](https://badge.fury.io/bo/cycle.svg)](http://badge.fury.io/bo/cycle)
Expand All @@ -39,38 +39,37 @@ DOM Rendering.
var Cycle = require('cyclejs');
var h = Cycle.h;

var HelloModel = Cycle.createModel(intent =>
({name$: intent.get('changeName$').startWith('')})
var Model = Cycle.createModel(Intent =>
({name$: Intent.get('changeName$').startWith('')})
});

var HelloView = Cycle.createView(model =>
var View = Cycle.createView(Model =>
({
vtree$: model.get('name$').map(name =>
vtree$: Model.get('name$').map(name =>
h('div', [
h('label', 'Name:'),
h('input', {
attributes: {'type': 'text'},
oninput: 'inputText$'
}),
h('input.field', {attributes: {type: 'text'}}),
h('h1', 'Hello ' + name)
])
)
})
);

var HelloIntent = Cycle.createIntent(view =>
({changeName$: view.get('inputText$').map(ev => ev.target.value)})
var User = Cycle.createDOMUser('.js-container');

var Intent = Cycle.createIntent(User =>
({changeName$: User.event$('.field', 'input').map(ev => ev.target.value)})
);

Cycle.createRenderer('.js-container').inject(HelloView);
HelloIntent.inject(HelloView).inject(HelloModel).inject(HelloIntent);
User.inject(View).inject(Model).inject(Intent).inject(User);
```

Notice that each of the 3 components has a neighbour component as input, and each outputs
an object containing RxJS Observables. At the bottom, the Renderer we created
subscribes to changes of `HelloView.get('vtree$')` and renders those virtual elements into
`.js-container` in the DOM. `inject()` just ties all three Model, View, and
Intent together by telling them that they depend on each other circularly.
Notice that each of the 4 parts has a preceding neighbour as input, and each outputs
an object containing RxJS Observables. The User is also of the same nature as the others:
it takes View's vtree$ as input, renders them to the DOM into `.js-container`, and outputs
event streams that can be accessed through `User.event$(selector, eventName)`. At the
bottom, `inject()` just ties all four Model, View, User, and Intent together by telling
them that they depend on each other circularly.

For advanced examples, check out [TodoMVC implemented in Cycle.js](https://github.com/staltz/todomvc-cycle) and [RxMarbles](https://github.com/staltz/rxmarbles).

Expand Down Expand Up @@ -98,10 +97,10 @@ still under 600 lines of code only.
Why would you use Cycle.js instead of other web frameworks such as Angular and React? Here
are a couple of strong reasons:

- **The only (yet) 100% reactive frontend framework.** The truth is, if you really wanted
- **The only (yet) 100% reactive frontend framework.** The truth is, if you really wanted
to apply reactive programming everywhere in a single page app, you would have no other
choice than Cycle. This is not yet another Flux library. I built it because it doesn't
exist elsewhere. I want to structure apps as observable event streams as much as possible,
exist elsewhere. I want to structure apps as observable event streams as much as possible,
while minimizing the use of `subscribe`, side effects, and `this`.
- **Separation of concerns.** The reactive pattern for Models, Views, and Intents makes it
possible for no component to have functions such as `updateSomething()` which inherently
Expand All @@ -110,14 +109,14 @@ are a couple of strong reasons:
have callbacks to handle events. Additionally, Rendering is separated from View. Contrary
to what you expect, a View in Cycle.js does not directly render anything to the browser.
Instead, it just outputs virtual DOM elements. This allows for testing without depending
on the DOM, besides other benefits such as being able to hypothetically swap the DOM
on the DOM, besides other benefits such as being able to hypothetically swap the DOM
Renderer with a Cocoa UI tree Renderer or whatever other target you wish.
- **Great unit testability.** Everything is a JavaScript function or a [DataFlowNode](https://github.com/staltz/cycle/blob/master/docs/data-flow-nodes.md),
so testing is mostly a matter of feeding input and inspecting the output.
- **Welcomes immutable and stateless programming.** Cycle.js is built for, in
combination with RxJS, a programming style that favors immutability and statelessness.
This allows code to be clearer and less prone to bugs. Apps written in Cycle.js are
`this`-less. See it for yourself, `this` cannot be found in [Cycle.js TodoMVC](https://github.com/staltz/todomvc-cycle/tree/master/js).
`this`-less. See it for yourself, `this` cannot be found in [Cycle.js TodoMVC](https://github.com/staltz/todomvc-cycle/tree/master/js).

## Community

Expand All @@ -139,7 +138,7 @@ number. After v1.0.0, we will follow [http://semver.org/](semver).
## Acknowledgements

- This project is a grateful recipient of the [Futurice Open Source sponsorship program](http://futurice.com/blog/sponsoring-free-time-open-source-activities).
- [@dobrite](https://github.com/dobrite) for [boilerplate reduction ideas](https://github.com/staltz/cycle/issues/56).
- [@dobrite](https://github.com/dobrite) for [boilerplate reduction ideas](https://github.com/staltz/cycle/issues/56).
- [@erykpiast](https://github.com/erykpiast) for pull requests.

## LICENSE
Expand Down
19 changes: 11 additions & 8 deletions rx-run/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- [`createModel`](#createModel)
- [`createView`](#createView)
- [`createIntent`](#createIntent)
- [`createRenderer`](#createRenderer)
- [`createDOMUser`](#createDOMUser)
- [`registerCustomElement`](#registerCustomElement)
- [`vdomPropHook`](#vdomPropHook)
- [`Rx`](#Rx)
Expand Down Expand Up @@ -114,33 +114,36 @@ Is a specialized case of `createDataFlowNode()`.

- - -

### <a id="createRenderer"></a> `createRenderer(container)`
### <a id="createDOMUser"></a> `createDOMUser(container)`

Returns a Renderer (a DataFlowSink) bound to a DOM container element. Contains an
`inject` function that should be called with a View as argument.
Returns a DOMUser (a DataFlowNode) bound to a DOM container element. Contains an
`inject` function that should be called with a View as argument. Events coming from
this user can be listened using `domUser.event$(selector, eventName)`. Example:
`domUser.event$('.mybutton', 'click').subscribe( ... )`

#### Arguments:

- `container :: String|HTMLElement` the DOM selector for the element (or the element itself) to contain the rendering of the VTrees.

#### Return:

*(Renderer)* a Renderer object containing an `inject(view)` function.
*(DOMUser)* a DOMUser object containing functions `inject(view)` and `event$(selector, eventName)`.

- - -

### <a id="registerCustomElement"></a> `registerCustomElement(tagName, dataFlowNode)`
### <a id="registerCustomElement"></a> `registerCustomElement(tagName, definitionFn)`

Informs Cycle to recognize the given `tagName` as a custom element implemented
as `dataFlowNode` whenever `tagName` is used in VTrees in a rendered View.
as `dataFlowNode` whenever `tagName` is used in VTrees in a View rendered to a
DOMUser.
The given `dataFlowNode` must export a `vtree$` Observable. If the `dataFlowNode`
expects Observable `foo$` as input, then the custom element's attribute named `foo`
will be injected automatically into `foo$`.

#### Arguments:

- `tagName :: String` a name for identifying the custom element.
- `dataFlowNode :: DataFlowNode` the implementation of the custom element.
- `definitionFn :: Function` the implementation for the custom element.

- - -

Expand Down
2 changes: 1 addition & 1 deletion rx-run/docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Page1Model ──> Page1View ────> RouteView <──── Page2View <
V
Renderer
DOMUser
```
The idea is to represent the current page (or route) in a RouteModel, then to use a RouteView that consumes the RouteModel, but also consumes Page1View and Page2View. In other words, RouteView should take three inputs. Internally, RouteView is just a switch which redirects only from Page1View if and only if the RouteModel has emitted 'page1' as the current page, or redirects only Page2View if RouteModel emits 'page2'. Then RouteIntent consumes Page1Intent and Page2Intent, the two latter should exports events indicating that the user wants to change the page. For instance, Page1Intent might have an event stream called `goToPage2$`. RouteIntent aggregates these types of events, and the RouteModel will listen to those.
Expand Down

0 comments on commit 1641a7f

Please sign in to comment.