github cyclejs/cyclejs v0.12.1
v0.12.1 - Revolutionary breaking change in Views

latest releases: unified-tag, v7.0.0, v7.0.0-rc8...
pre-release9 years ago

Replace Renderer with DOMUser, allowing Views to be event agnostic.

This breaking change version implements #73. From now on, instead of using onclick: 'click$' in properties in Views, you instead select which events you are interested in, with User.event$('.myelement', 'click') inside Intents.

This also brings an interesting twist to the overall MVI architecture. It's now MVUI: Model-View-User-Intent. There is no more Renderer (a DataFlowSink), now we have DOMUser (a DataFlowNode). This means that the DOMUser acts as a function: takes the View's vtree$ as input, renders that to the DOM as a side-effect, and outputs DOM events. You can select which DOM event stream by calling User.event$(selector, eventName), and of course, the output is an RxJS Observable.

Migration guide:
BEFORE

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

var HelloIntent = Cycle.createIntent(View =>
  ({changeName$: View.get('inputText$').map(ev => ev.target.value)})
);

AFTER

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

// Notice this, replaces Renderer, but has basically the same API
var User = Cycle.createDOMUser('.js-container');

// Notice Intent from now on should always take a DOMUser as input, NOT a View
var Intent = Cycle.createIntent(User =>
  // Notice the usage of User.event$()
  ({changeName$: User.event$('.field', 'input').map(ev => ev.target.value)})
);

And since DOMUser is now a DataFlowNode in the middle of the cycle, the injection part becomes:

User.inject(View).inject(Model).inject(Intent).inject(User);

Custom elements

Are now slightly simpler to define. No need for a View as an implementation, and it doesn't need to return vtree$ anymore since it will have it's own internal DOMUser.

             // function, not createView. And notice element here
var HelloComponent = function(element, Properties) {
  var HelloModel = Cycle.createModel((Properties, Intent) =>
    ({
      name$: Properties.get('name$')
        .merge(Intent.get('changeName$'))
        .startWith('')
    })
  );

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

  // Notice DOMUser internal here. It should use the element from above
  var HelloUser = Cycle.createDomUser(element);

  var HelloIntent = Cycle.createIntent(User =>
    ({changeName$: User.events('.myinput', 'click').map(ev => ev.target.value)})
  );

  HelloIntent
  .inject(HelloUser)
  .inject(HelloView)
  .inject(HelloModel)
  .inject(Properties, HelloIntent);

  return {
    // notice no more vtree$ exported here
    // this part is only used for custom events exported out of this component
  };
};

Cycle.registerCustomElement('hello', HelloComponent);

Basically, v0.12 is the user as a function in your UI architecture, and the View focusing only on what it does best: rendering.

Don't miss a new cyclejs release

NewReleases is sending notifications on new releases.