mithril-vndb/docs/v1.x-migration.md
maks feltrin ce93bdbb79 rename ctrl to state
I am not sure you agree, but i find the name `state` a bit less confusing that the name `ctrl` used in v 0.2.x.
thank you and kind regards
2016-10-22 04:04:47 +02:00

10 KiB

Migrating from v0.2.x to v1.x

v1.x is largely API-compatible with v0.2.x, but there are a few breaking changes.

m.component removed

In v0.2.x components could be created using either m(component) or m.component(component). v1.x only supports m(component).

v0.2.x

// These are equivalent
m.component(component);
m(component);

v1.x

m(component);

config function

In v0.2.x mithril provided a single lifecycle method, config. v1.x provides much more fine-grained control over the lifecycle of a vnode.

v0.2.x

m("div", {
    config : function(element, isInitialized) {
        // runs on each redraw
        // isInitialized is a boolean representing if the node has been added to the DOM
    }
});

v1.x

More documentation on these new methods is available in lifecycle-methods.md.

m("div", {
    // Called before the DOM node is created
    oninit : function(vnode) { /*...*/ },
    // Called after the DOM node is created
    oncreate : function(vnode) { /*...*/ },
    // Called before the node is updated, return false to cancel
    onbeforeupdate : function(vnode, old) { /*...*/ },
    // Called after the node is updated
    onupdate : function(vnode) { /*...*/ },
    // Called before the node is removed, call done() when ready for the node to be removed from the DOM
    onbeforeremove : function(vnode, done) { /*...*/ },
    // Called before the node is removed, but after onbeforeremove calls done()
    onremove : function(vnode) { /*...*/ }
});

If available the DOM-Element of the vnode can be accessed at vnode.dom.

Cancelling redraw from event handlers

m.mount() and m.route() still automatically redraw after a DOM event handler runs. Cancelling these redraws from within your event handlers is now done by setting the redraw property on the passed-in event object to false.

v0.2.x

m("div", {
    onclick : function(e) {
        m.redraw.strategy("none");
    }
})

v1.x

m("div", {
    onclick : function(e) {
        e.redraw = false;
    }
})

Component controller function

In v1.x there is no more controller property in components, use oninit instead.

v0.2.x

m.mount(document.body, {
    controller : function() {
        var ctrl = this;

        ctrl.fooga = 1;
    },

    view : function(ctrl) {
        return m("p", ctrl.fooga);
    }
});

v1.x

m.mount(document.body, {
    oninit : function(vnode) {
        vnode.state.fooga = 1;
    },

    view : function(vnode) {
        return m("p", vnode.state.fooga);
    }
});

// OR

m.mount(document.body, {
    oninit : function(vnode) {
        var state = this;  // this is bound to vnode.state by default

        state.fooga = 1;
    },

    view : function(vnode) {
        var state = this; // this is bound to vnode.state by default

        return m("p", state.fooga);
    }
});

Component arguments

Arguments to a component in v1.x must be an object, simple values like String/Number/Boolean will be treated as text children. Arguments are accessed within the component by reading them from the vnode.attrs object.

v0.2.x

var component = {
    controller : function(options) {
        // options.fooga === 1
    },

    view : function(ctrl, options) {
        // options.fooga == 1
    }
};

m("div", m.component(component, { fooga : 1 }));

v1.x

var component = {
    oninit : function(vnode) {
        // vnode.attrs.fooga === 1
    },

    view : function(vnode) {
        // vnode.attrs.fooga == 1
    }
};

m("div", m(component, { fooga : 1 }));

view() parameters

In v0.2.x view functions are passed a reference to the controller instance and (optionally) any options passed to the component. In v1.x they are passed only the vnode, exactly like the controller function.

v0.2.x

m.mount(document.body, {
    controller : function() {},

    view : function(ctrl, options) {
        // ...
    }
});

v1.x

m.mount(document.body, {
    oninit : function(vnode) {
        // ...
    },
    
    view : function(vnode) {
        // Use vnode.state instead of ctrl
        // Use vnode.attrs instead of options
    }
});

Passing components to m()

In v0.2.x you could pass components as the second argument of m() w/o any wrapping required. To help with consistency in v1.x they must always be wrapped with a m() invocation.

v0.2.x

m("div", component);

v1.x

m("div", m(component));

Passing vnodes to m.mount() and m.route()

In v0.2.x, m.mount(element, component) tolerated vnodes as second arguments instead of components (even though it wasn't documented). Likewise, m.route(element, defaultRoute, routes) accepted vnodes as values in the routes object.

In v1.x, components are required instead in both cases.

v0.2.x

m.mount(element, m('i', 'hello'));
m.mount(element, m(Component, attrs));

m.route(element, '/', {
    '/': m('b', 'bye')
})

v1.x

m.mount(element, {view: function () {return m('i', 'hello')}});
m.mount(element, {view: function () {return m(Component, attrs)}});

m.route(element, '/', {
    '/': {view: function () {return m('b', 'bye')}}
})

m.route.mode

In v0.2.x the routing mode could be set by assigning a string of "pathname", "hash", or "search" to m.route.mode. In v.1.x it is replaced by m.route.prefix(prefix) where prefix can be #, ?, or an empty string (for "pathname" mode). The new API also supports hashbang (#!), which is the default, and it supports non-root pathnames and arbitrary mode variations such as querybang (?!)

v0.2.x

m.route.mode = "pathname";
m.route.mode = "search";

v1.x

m.route.prefix("");
m.route.prefix("?");

m.route() and anchor tags

Handling clicks on anchor tags via the mithril router is similar to v0.2.x but uses a new lifecycle method and API.

v0.2.x

// When clicked this link will load the "/path" route instead of navigating
m("a", {
    href   : "/path",
    config : m.route
})

v1.x

// When clicked this link will load the "/path" route instead of navigating
m("a", {
    href     : "/path",
    oncreate : m.route.link
})

Reading/writing the current route

In v0.2.x all interaction w/ the current route happened via m.route(). In v1.x this has been broken out into two functions.

v0.2.x

// Getting the current route
m.route()

// Setting a new route
m.route("/other/route");

v1.x

// Getting the current route
m.route.get();

// Setting a new route
m.route.set("/other/route");

Accessing route params

In v0.2.x reading route params was all handled through the m.route.param() method. In v1.x any route params are passed as the attrs object on the vnode passed as the first argument to lifecycle methods/view.

v0.2.x

m.route(document.body, "/booga", {
    "/:attr" : {
        view : function() {
            m.route.param("attr"); // "booga"
        }
    }
});

v1.x

m.route(document.body, "/booga", {
    "/:attr" : {
        oninit : function(vnode) {
            vnode.attrs.attr; // "booga"
        },
        view : function(vnode) {
            vnode.attrs.attr; // "booga"
        }
    }
});

m.request

m.request now returns an m.prop stream instead of a promise. The main difference is you'll have to use .run to get similar functionality as a promise's .then:

v0.2.x

m.request({ method: 'GET', url: 'https://api.github.com/' })
    .then(function (responseBody) {
        return m.request({ method: 'GET', url: responseBody.emojis_url });
    })
    .then(function (emojis) {
        console.log("+1 url:", emojis['+1']);
    });

v1.x

m.request({ method: 'GET', url: 'https://api.github.com/' })
    .run(function (responseBody) {
        return m.request({ method: 'GET', url: responseBody.emojis_url });
    })
    .run(function (emojis) {
        console.log("+1 url:", emojis['+1']);
    });

The equivalent of m.sync is now m.prop.merge:

v0.2.x

m.sync([
    m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
    m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
])
    .then(function (users) {
        console.log("Contributors:", users[0].name, "and", users[1].name);
    });

v1.x

m.prop.merge([
    m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
    m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
])
    .run(function (users) {
        console.log("Contributors:", users[0].name, "and", users[1].name);
    });

Additionally, if the extract option is passed to m.request the return value of the provided function will be passed to the m.prop stream directly, and any deserialize callback is ignored.

In v0.2.x when generating SVGs using m() the xlink namespace would automatically be added on certain attributes. For consistency in v1.x if you want to namespace an attribute you must do it yourself.

v0.2.x

m("svg",
    // the `href` attribute is namespaced automatically
	m("image[href='image.gif']")
)

v1.x

m("svg",
    // User-specified namespace on the `href` attribute
	m("image[xlink:href='image.gif']")
)