Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running js code after all dom rendered in Cycle.js

I want to use Foundation's reveal plugin to display a nice modal in a Cycle.js app.

After playing with Webpack and it's configuration for hours, I figured how to propertly import foundation's js into my (es6) app.

However, since I'm dealing with virtual dom, the actual html does not exist when foundation's js is initialized.

Here's my component's code:

import Rx from 'rx';
import {div} from '@cycle/dom';

import $ from 'jquery';
import {foundation} from 'foundation-sites/js/foundation.core';
import 'foundation-sites/js/foundation.util.keyboard';
import 'foundation-sites/js/foundation.util.box';
import 'foundation-sites/js/foundation.util.triggers';
import 'foundation-sites/js/foundation.util.mediaQuery';
import 'foundation-sites/js/foundation.util.motion';
import 'foundation-sites/js/foundation.reveal';

require('./styles/configuration-modal.scss');

function ConfigurationModal(sources) {

    const values$ = sources.props$;

    const vtree$ = Rx.Observable.just(1).map(() => {
        return div(
            '#config-modal.reveal',
            {attributes: {
                'data-reveal': ''
            }},
            ['hello']
        );
    });

    return {
        DOM: vtree$,
        values$
    };
}

$(function() {
    $.fn.foundation = foundation;
    $(document).foundation();
});

export default ConfigurationModal;

My main.js:

import Rx from 'rx';
import Cycle from '@cycle/core';
import {makeDOMDriver, div} from '@cycle/dom';
import ConfigurationModal from './components/ConfigurationModal/index';

require('./styles/index.scss');

function main(sources) {
    const props$ = Rx.Observable.of({
        pomodoroDuration: 25 * 60,
        shortBreakDuration: 5 * 60,
        longBreakDuration: 15 * 60
    });
    const configurationModal = ConfigurationModal({
        DOM: sources.DOM,
        props$
    });

    return {
        DOM: configurationModal.DOM,
    };
}

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

Now, if I run $(document).foundation() in the console once the app is initialized, things work as expected. But in the app code, since the dom is virtual and the actual html has not yet been injected, nothing appears.

How could I run js code after the app was fully initialized and the dom actually populated?

like image 516
Thibault J Avatar asked Apr 10 '16 13:04

Thibault J


1 Answers

In Cycle, there's a useful feature of the DOM driver that allows you to subscribe to updates for elements.

In this case, you can run code after the app starts up like this:

DOM
 .select(':root')
 .element()
 .take(1)
 .subscribe((element) => $(document).foundation())

You could add that to your main, and that should solve your problem.

However, in Cycle it's idomatic to encapsulate all of your subscribes in drivers, as they're usually side effects. In this case, you can make a driver to start Foundation, like so:

function startFoundation () {
  $(document).foundation();
}

function foundationDriver(sink$) {
  return sink$.take(1).subscribe(startFoundation);
}

And in your main.js:

function main(sources) {
    // ...

    const startup$ = sources.DOM.select(':root').element().take(1);

    return {
        DOM: configurationModal.DOM,

        FoundationStartup: startup$
    };
}

Cycle.run(main, {
    DOM: makeDOMDriver('#app'),
    FoundationStartup: foundationDriver
});
like image 113
Widdershin Avatar answered Oct 09 '22 04:10

Widdershin