kristian nissen
kristian nissen

Reputation: 2907

Loading json data into CycleJS app

I'm trying to wrap my head around CycleJS but I'm stuck! I'm trying to put a small app together that loads a JSON file containing an array of objects, but I can't get the app to execute the http request

Here in my code so far

'use strict';

import Rx from 'rx';
import Cycle from '@cycle/core';
import {h, makeDOMDriver} from '@cycle/dom';
import {makeHTTPDriver} from '@cycle/http';

function intent(DOM) {
    return {
        edit: DOM.select('div')
            .events('click')
            .map(evt => evt.target.value),
        add: DOM.select('div')
            .events('click')
            .map(evt => evt.target.value)
    };
}

function model(actions, response) {
    return response
}

function view(state) {
    return state.map(item => {
        h('div', [
            item.map(todo => h('div', todo.title))
        ])
    });
}

function main(sources) {
    const URL = 'http://localhost:3000/js/planinator/data.json';

    let response = sources.HTTP
        .mergeAll()
        .map(res => res.body)
        .startWith([]);

    const actions = intent(sources.DOM);
    const state = model(actions, response);

    return {
        DOM: view(state),
        HTTP: Rx.Observable.of(URL)
    }
}

Cycle.run(main, {
    DOM: makeDOMDriver('#appmount'),
    HTTP: makeHTTPDriver()
});

What I'm trying to achieve is; load JSON data and render it as div tags for now.

When I run the code in chrome I get this in the console

bundle.js:14182 TypeError: Cannot read property 'subscribe' of undefined(…)

I have checked the questions found by stackoverflow, but they didn't get me ay further

Upvotes: 3

Views: 222

Answers (1)

TylorS
TylorS

Reputation: 139

Welcome to Cycle.js :)

The first issue that I notice, is that the HTTP driver will first expect an Observable of an http request, where at the moment you are simply passing it a string of a url. This can be done by wrapping the URL in an Observable using Rx's Rx.Observable.of(URL)

function main(sources) {
  ...
  return {
    ...
    HTTP: Rx.Observable.of(URL)
  }
 }

Next, I notice that your request (which is actually your response!), is misusing the HTTP driver a bit. The HTTP driver returns a higher-order Observable back to main, or in other words, it returns an Observable that contains other observables. This can easily be rectified with a mergeAll() or a switch(). mergeAll() and switch() effectively takes a higher-order observable, and 'flattens' it into an observable that contains the events of the 'inner' observables.

let response = sources.HTTP
  .mergeAll()
  .filter(....)
  .map(....)
  .startWith([])

The distinction between the two methods are subtle, but very important. mergeAll() has a concurrency of Infinity. What does that even mean? mergeAll() will take all of the inner observables and subscribe to them, and push their events to the containing observable, while never unsubscribing. switch() is similar but with the concurrency of 1. It will only subscribe to the latest inner observable, whilst disposing of the previous.

I can't be for sure this answers all of your question, but I hope it at least gets your started in the right direction! :)

Upvotes: 2

Related Questions