Search code examples
javascripthtmlrxjscyclejs

Cycle.js app uncaught type error


I'm trying to learn Cycle.js and must say that I'm finding it quite interesting. I'm trying to create a simple app where I have a input and a ul. Every time write some value to the input and I press enter I want to add a new li with the value to the ul, but its failing with the following error.

Uncaught TypeError: observables[_name2].doOnError is not a function

var view = function (state) {
    return CycleDOM.body([
        CycleDOM.input({ type: 'text', value: '' }),
        CycleDOM.ul({ className: 'text' },
            state.map(function (value) {
                CycleDOM.li({ innerText: value });
            }))
    ]);
};

var intent = function (DOM) {
    return DOM.select('input[type=text]').events('keydown').filter(function (ev) {
        return ev.which == 13 && ev.target.value.trim().length > 0;
    }).map(function (ev) {
        return ev.target.value;
    });
};

var model = function (action) {
    return action.startWith('');
};

var main = function (sources) {
    var actions = intent(sources.DOM);
    var state = model(actions);
    var sinks = {
        DOM: view(state)
    };
    return sinks;
}

var drivers = {
    DOM: CycleDOM.makeDOMDriver(document.body)
};

Cycle.run(main, drivers);

Solution

  • First, it's good to see that people are interested in Cycle!

    You are missing some points here and that's why you're having some struggle.

    You might have not fully understood the concept of reactive programming yet. You should read The introduction to Reactive Programming you've been missing by the creator of Cycle and watch his videos about Cycle. They really help understanding how Cycle works on the inside.

    Also, you could adopt the naming convention of Cycle, it really helps. A stream/observable should end with a $, like

    var click$ = DOM.select('a').events('click');
    

    As @juanrpozo said your main issue is in your view function because it returns a virtual tree instead of an observable of virtual tree. Also it is important that you understand that the state variable is an observable, not a javascript array. That's why I think you aren't comfortable with Rx yet. You think you're mapping an array, but actually your mapping an observable, hence returning another observable, not an array. But as the DOM sink should be an observable, that's perfect, you'd just have to wrap your VTree in the map:

    var view = function (state$) {
        return state$.map(function (values) {
            CycleDOM.body([
                CycleDOM.input({ type: 'text', value: '' }),
                CycleDOM.ul({ className: 'text' }, values.map(function (value) {
                    CycleDOM.li(value);
                }))
            ])
        };
    }
    

    Another issue is your state$ management. You have to understand that state$ is a stream of consecutive states of your component. It's kinda hard to explain this on stackoverflow, but if you read/watch the resources I sent you you'll get it without any problem.

    I made you a jsbin of your code once corrected and changed a bit to respect a bit more the Intent/Model/View convention.

    You also had other errors, but those were the most important.