1.4.0 • Published 8 years ago

cycle-falcor v1.4.0

Weekly downloads
2
License
MIT
Repository
github
Last release
8 years ago

Cycle.js Falcor Driver

A Cycle.js Driver for Netflix Falcor data platform. The model uses HttpDataSource with fixed settings for now: model.json route with 60 seconds timeout.

npm install --save cycle-falcor

Request object properties

  • method (String) - required - model method (get, getValue, set, setValue, call)
  • path (Array) - required - array representing model's path (for example 'items', 'latest')
  • resKey (String) - key with which the response will be wrapped
  • invalidatePath (Array) - array with model path to invalidate upon received HttpDataSource response
  • eager (Boolean) - by default, response$ observables are lazy - setting this option to true would make them eager with replay. Example use case: the request$ is a sequence of merged observables where some of those observables emit request object with path property that must contain response values generated by one or more other request$ observables.

Examples

A simple example of fetching and displaying data from 'items', 'latest' path that gives a JSON graph response in the following format:

{
    "json": {
        "items": {
            "$__path": ["items"],
            "latest": {
                "$__path": ["items", "latest"],
                "0": {
                    "name": "Item 1",
                    "description": "Description of item 1",
                    "price": 450,
                    "$__path": ["item", "latest", 0]
                },
                "1": {
                    "name": "Item 2",
                    "description": "Description of item 2",
                    "price": 220,
                    "$__path": ["item", "latest", 1]
                }
            }
        }
    }
}
import Cycle from '@cycle/core';
import {Observable} from 'rx';
import {makeDOMDriver, div} from '@cycle/dom';
import {makeFalcorDriver} from 'cycle-falcor';

function getItems(res) {
    const items = res.json.items.latest;
    return Object.keys(items)
        .filter(key => key !== '$__path')
        .map(key => items[key]);
}

function main(sources) {
    const request$ = Observable.just({
        method: 'get',
        path: ['items', 'latest']
    });

    const state$ = sources.Falcor
        .mergeAll()
        .map(res => getItems(res));

    const vtree$ = state$.map(items => {
        return div('.items', [
            items.map(item => {
                return div('.row', [
                    div('.column', item.name),
                    div('.column', item.description),
                    div('.column', item.price)
                ]);
            })
        ]);
    });

    return {
        DOM: vtree$,
        Falcor: request$
    };
}

Cycle.run(main, {
    DOM: makeDOMDriver('#app'),
    Falcor: makeFalcorDriver()
});

More complex example where the request objects of two request observables are dependent on responses generated by other request observables. To be more precise, lengthRequest$ depends on the item emitted by intent$ (search query), and listRequest$ depends on search query and the actual length value - response generated by lengthRequest$ request observable. This is where the eager request option comes in handy, as multiple observables get subscribed to each response observable.

import Cycle from '@cycle/core';
import {Observable} from 'rx';
import {makeDOMDriver, div, input} from '@cycle/dom';
import {makeFalcorDriver} from 'cycle-falcor';

function makeRequest(intent$, Falcor) {
    const makeLengthRequest = searchQuery => {
        return Observable.just({
            method: 'get',
            path: ['items', 'length', searchQuery],
            resKey: 'lengthRes',
            eager: true
        });
    };

    const makeListRequest = (searchQuery, lengthRes) => {
        if ((searchQuery || searchQuery === '') && lengthRes) {
            let length = lengthRes.json.items.length[searchQuery];
            let range = { from: 0, to: length };

            return Observable.just({
                method: 'get',
                path: ['items', 'list', searchQuery, range],
                resKey: 'listRes',
                eager: true
            });
        }

        return Observable.empty();
    };

    const lengthRequest$ = Observable.merge(Falcor.mergeAll(), intent$)
        .filter(item => typeof item === 'string')
        .flatMapLatest(searchQuery => makeLengthRequest(searchQuery));

    const listRequest$ = Observable.merge(Falcor.mergeAll(), intent$)
        .filter(item => typeof item === 'string' || item.hasOwnProperty('lengthRes'))
        .scan((acc, item) => {
            if (item.lengthRes) {
                acc.lengthRes = item.lengthRes;
            } else {
                acc.searchQuery = item;
            }

            return acc;
        }, {})
        .flatMapLatest(item => makeListRequest(item.searchQuery, item.lengthRes));

    return Observable.merge(lengthRequest$, listRequest$);
}

function getItems(listRes, searchQuery) {
    const items = listRes.json.items.list[searchQuery];
    return Object.keys(items)
        .filter(key => key !== '$__path')
        .map(key => items[key]);
}

function main(sources) {
    const intent$ = sources.DOM.select('.search')
        .events('input')
        .debounce(200)
        .map(ev => ev.target.value)
        .startWith('')
        .replay(null, 1)
        .refCount();

    const request$ = makeRequest(intent$, sources.Falcor);

    const state$ = Observable.merge(sources.Falcor.mergeAll(), intent$)
        .scan((acc, item) => {
            if (item.listRes) {
                acc.items = getItems(item.listRes, acc.searchQuery);
            }

            if (typeof item === 'string') {
                acc.searchQuery = item;
            }

            return acc;
        }, { items: [], searchQuery: '' })
        .map(data => data.items);

    const vtree$ = state$.map(items => {
        return div('.container', [
            div('.search-box', [
                input('.search', {
                    type: 'text'
                })
            ]),
            div('.items', [
                items.map(item => {
                    return div('.row', [
                        div('.column', item.name),
                        div('.column', item.description),
                        div('.column', item.price)
                    ]);
                })
            ])
        ]);
    });

    return {
        DOM: vtree$,
        Falcor: request$
    };
}

Cycle.run(main, {
    DOM: makeDOMDriver('#app'),
    Falcor: makeFalcorDriver()
});
1.4.0

8 years ago

1.3.0

8 years ago

1.2.0

8 years ago

1.1.0

8 years ago

1.0.0

8 years ago