From 9e8dc6998d2dddf4df7227f6d2cceea8655a19f0 Mon Sep 17 00:00:00 2001 From: Leo Horie Date: Thu, 9 Apr 2015 22:44:45 -0400 Subject: [PATCH] rework api --- docs/components.md | 142 +++++++++---------- docs/integration.md | 10 +- docs/layout/api.html | 2 +- docs/layout/index.html | 8 +- docs/mithril.component.md | 201 ++++++++++++++++----------- docs/mithril.mount.md | 10 +- docs/mithril.prop.md | 4 +- docs/mithril.redraw.md | 16 +-- docs/mithril.route.md | 12 +- dragdrop.html | 94 +++++++++++++ guide.html | 70 ++++++++++ mithril.js | 46 +++--- modulator-test.html | 274 ++++++++++++++++++++++++++++++++++++ tests/mithril-tests.js | 286 ++++++++++++++++++++------------------ 14 files changed, 830 insertions(+), 345 deletions(-) create mode 100644 dragdrop.html create mode 100644 guide.html create mode 100644 modulator-test.html diff --git a/docs/components.md b/docs/components.md index c1e013e1..467ca2f2 100644 --- a/docs/components.md +++ b/docs/components.md @@ -41,7 +41,7 @@ Here, we've defined a class called `Contact`. A contact has an id, a name and an One way of organizing components is to use component parameter lists to send data downstream, and to define events to bubble data back upstream to a centralized module who is responsible for interfacing with the model layer. ```javascript -var ContactsWidget = m.component({ +var ContactsWidget = { controller: function update() { this.contacts = Contact.list() this.save = function(contact) { @@ -50,13 +50,13 @@ var ContactsWidget = m.component({ }, view: function(ctrl) { return [ - ContactForm({onsave: ctrl.save}), - ContactList({contacts: ctrl.contacts}) + m.component(ContactForm, {onsave: ctrl.save}), + m.component(ContactList, {contacts: ctrl.contacts}) ] } -}) +} -var ContactForm = m.component({ +var ContactForm = { controller: function(args) { this.contact = m.prop(args.contact || new Contact()) }, @@ -73,9 +73,9 @@ var ContactForm = m.component({ m("button[type=button]", {onclick: args.onsave.bind(this, contact)}, "Save") ]) } -}) +} -var ContactList = m.component({ +var ContactList = { view: function(ctrl, args) { return m("table", [ args.contacts().map(function(contact) { @@ -87,9 +87,9 @@ var ContactList = m.component({ }) ]) } -}) +} -m.module(document.body, ContactsWidget()) +m.mount(document.body, ContactsWidget) ``` In the example above, there are 3 components. `ContactsWidget` is the top level module being rendered to `document.body`, and it is the module that has the responsibility of talking to our Model entity `Contact`, which we defined earlier. @@ -123,7 +123,7 @@ Another way of organizing code is to distribute concrete responsibilities across Here's a refactored version of the sample app above to illustrate: ```javascript -var ContactForm = m.component({ +var ContactForm = { controller: function() { this.contact = m.prop(new Contact()) this.save = function(contact) { @@ -143,7 +143,7 @@ var ContactForm = m.component({ m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save") ]) } -}) +} var ContactList = { controller: function() { @@ -163,8 +163,8 @@ var ContactList = { } m.route(document.body, "/", { - "/list": ContactList(), - "/create": ContactForm() + "/list": ContactList, + "/create": ContactForm }) ``` @@ -200,16 +200,16 @@ var Observable = function() { }.call() -var ContactsWidget = m.component({ +var ContactsWidget = { view: function(ctrl) { return [ - m.module(ContactForm), - m.module(ContactList) + ContactForm, + ContactList ] } -}) +} -var ContactForm = m.component({ +var ContactForm = { controller: function() { this.contact = m.prop(new Contact()) this.save = function(contact) { @@ -229,9 +229,9 @@ var ContactForm = m.component({ m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save") ]) } -}) +} -var ContactList = m.component({ +var ContactList = { controller: Observable.register(function() { this.contacts = Contact.list() }), @@ -246,9 +246,9 @@ var ContactList = m.component({ }) ]) } -}) +} -m.module(document.body, ContactsWidget()) +m.module(document.body, ContactsWidget) ``` In this iteration, both the `ContactForm` and `ContactList` components are now children of the `ContactsWidget` component and they appear simultaneously on the same page. @@ -311,19 +311,19 @@ It's of course possible to use both aggregation of responsibility and the observ The example below shows a variation of the contacts app where `ContactForm` is responsible for saving. ```javascript -var ContactsWidget = m.component({ +var ContactsWidget = { controller: Observable.register(["updateContact"], function() { this.contacts = Contact.list() }), view: function(ctrl) { return [ - ContactForm(), - ContactList({contacts: ctrl.contacts}) + m.component(ContactForm), + m.component(ContactList, {contacts: ctrl.contacts}) ] } -}) +} -var ContactForm = m.component({ +var ContactForm = { controller: function(args) { this.contact = m.prop(new Contact()) this.save = function(contact) { @@ -343,9 +343,9 @@ var ContactForm = m.component({ m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save") ]) } -}) +} -var ContactList = m.component({ +var ContactList = { view: function(ctrl, args) { return m("table", [ args.contacts().map(function(contact) { @@ -357,9 +357,9 @@ var ContactList = m.component({ }) ]) } -}) +} -m.mount(document.body, ContactsWidget()) +m.mount(document.body, ContactsWidget) ``` Here, the data fetching is still centralized in the top-level component, so that we can avoid duplicate AJAX requests when fetching data. @@ -379,20 +379,20 @@ Observable.on(["saveContact"], function(data) { }) //ContactsWidget is the same as before -var ContactsWidget = m.component({ +var ContactsWidget = { controller: Observable.register(["updateContact"], function() { this.contacts = Contact.list() }), view: function(ctrl) { return [ - ContactForm(), - ContactList({contacts: ctrl.contacts}) + m.component(ContactForm), + m.component(ContactList, {contacts: ctrl.contacts}) ] } -}) +} //ContactList no longer calls `Contact.save` -var ContactForm = m.component({ +var ContactForm = { controller: function(args) { this.contact = m.prop(new Contact()) this.save = function(contact) { @@ -412,10 +412,10 @@ var ContactForm = m.component({ m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save") ]) } -}) +} //ContactList is the same as before -var ContactList = m.component({ +var ContactList = { view: function(ctrl, args) { return m("table", [ args.contacts().map(function(contact) { @@ -427,9 +427,9 @@ var ContactList = m.component({ }) ]) } -}) +} -m.mount(document.body, ContactsWidget()) +m.mount(document.body, ContactsWidget) ``` Here we've moved `Contact.save(contact).then(Observable.broadcast("updateContact"))` out of the `ContactForm` component and into the model layer. In its place, `ContactForm` merely emits an action, which is then handled by this model layer observer. @@ -445,7 +445,35 @@ Here's an example of a not-so-trivial component: a drag-n-drop file uploader. In These two functions are here to illustrate the ability to expose APIs to component consumers that complement the component's user interface. By bundling model methods in the component, we avoid hard-coding how files are handled once they're dropped in, and instead, we provide a useful library of functions that can be consumed flexibly to meet the demands on an application. ```javascript -var Uploader = m.component({ +var Uploader = { + upload: function(options) { + var formData = new FormData + for (var key in options.data) { + for (var i = 0; i < options.data[key].length; i++) { + formData.append(key, files[i]) + } + } + + //simply pass the FormData object intact to the underlying XMLHttpRequest, instead of JSON.stringify'ing it + options.serialize = function(value) {return value} + options.data = formData + + return m.request(options) + }, + serialize: function(files) { + var promises = Array.prototype.slice.call(files).map(function(file) { + var deferred = m.deferred() + + var reader = new FileReader + reader.readAsDataURL() + reader.onloadend = function(e) { + deferred.resolve(e.result) + } + reader.onerror = deferred.reject + return deferred + }) + return m.sync(promises) + }, controller: function(args) { this.noop = function(e) { e.preventDefault() @@ -460,34 +488,6 @@ var Uploader = m.component({ view: function(ctrl, args) { return m(".uploader", {ondragover: ctrl.noop, ondrop: ctrl.update}) } -}) -Uploader.upload = function(options) { - var formData = new FormData - for (var key in options.data) { - for (var i = 0; i < options.data[key].length; i++) { - formData.append(key, files[i]) - } - } - - //simply pass the FormData object intact to the underlying XMLHttpRequest, instead of JSON.stringify'ing it - options.serialize = function(value) {return value} - options.data = formData - - return m.request(options) -} -Uploader.serialize = function(files) { - var promises = Array.prototype.slice.call(files).map(function(file) { - var deferred = m.deferred() - - var reader = new FileReader - reader.readAsDataURL() - reader.onloadend = function(e) { - deferred.resolve(e.result) - } - reader.onerror = deferred.reject - return deferred - }) - return m.sync(promises) } ``` @@ -506,7 +506,7 @@ var Demo1 = { view: function(ctrl) { return [ m("h1", "Uploader demo"), - Uploader({onchange: ctrl.upload}) + m.component(Uploader, {onchange: ctrl.upload}) ] } } @@ -534,7 +534,7 @@ var Demo2 = { return [ m("h1", "Uploader demo"), m("form", [ - Uploader({onchange: ctrl.files}), + m.component(Uploader, {onchange: ctrl.files}), m("button[type=button]", {onclick: ctrl.save}) ]) ] diff --git a/docs/integration.md b/docs/integration.md index 55d8c6b5..b470e66e 100644 --- a/docs/integration.md +++ b/docs/integration.md @@ -8,7 +8,7 @@ The example below shows a simple component that integrates with the [select2 lib ```javascript //Select2 component (assumes both jQuery and Select2 are included in the page) -var Select2 = m.component({ +var Select2 = { //this view implements select2's `` element and its 3rd party plugin after route changes, since the `` doesn't change - var Component1 = m.component({ + var Component1 = { controller: function() { m.redraw.strategy("diff") }, @@ -74,9 +74,9 @@ Common reasons why one might need to change redraw strategy are: m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library ]) } - }) + } - var Component2 = m.component({ + var Component2 = { controller: function() { m.redraw.strategy("diff") }, @@ -86,7 +86,7 @@ Common reasons why one might need to change redraw strategy are: m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library ]) } - }) + } m.route(document.body, "/foo", { "/foo": Component1, diff --git a/docs/mithril.route.md b/docs/mithril.route.md index 8a512e18..1fc8bcd3 100644 --- a/docs/mithril.route.md +++ b/docs/mithril.route.md @@ -52,14 +52,14 @@ The example below shows a route that takes an `userID` parameter ```javascript //a sample component -var Dashboard = m.component({ +var Dashboard = { controller: function() { return {id: m.route.param("userID")} }, view: function(controller) { return m("div", controller.id); } -}) +} //setup routes to start w/ the `#` symbol m.route.mode = "hash"; @@ -139,7 +139,7 @@ var dir = m.route.param("dir") // "desc" If a component's controller implements an instance method called `onunload`, this method will be called when a route changes. ```javascript -var Home = m.component({ +var Home = { controller: function() { return { onunload: function() { @@ -150,12 +150,12 @@ var Home = m.component({ view: function() { return m("div", "Home") } -}); +}; -var Dashboard = m.component({ +var Dashboard = { controller: function() {}, view: function() {} -}); +}; //go to the default route (home) m.route(document.body, "/", { diff --git a/dragdrop.html b/dragdrop.html new file mode 100644 index 00000000..a7e70dd3 --- /dev/null +++ b/dragdrop.html @@ -0,0 +1,94 @@ + +
+ + \ No newline at end of file diff --git a/guide.html b/guide.html new file mode 100644 index 00000000..17bfd1f1 --- /dev/null +++ b/guide.html @@ -0,0 +1,70 @@ + + + \ No newline at end of file diff --git a/mithril.js b/mithril.js index 0704ffd7..e35d966a 100644 --- a/mithril.js +++ b/mithril.js @@ -238,7 +238,7 @@ var m = (function app(window, undefined) { else if (data != null && dataType === OBJECT) { var controllerConstructors = [], controllers = [] while (data.view) { - var controllerConstructor = data.controller.$original || data.controller + var controllerConstructor = (data.controller || {}).$original || data.controller || function() {} var controllerIndex = cached.controllerConstructors ? cached.controllerConstructors.indexOf(controllerConstructor) : -1 var controller = controllerIndex > -1 ? cached.controllers[controllerIndex] : new (data.controller || function() {}) var key = data && data.attrs && data.attrs.key @@ -543,28 +543,25 @@ var m = (function app(window, undefined) { return gettersetter(store) }; - var roots = [], modules = [], controllers = [], lastRedrawId = null, lastRedrawCallTime = 0, computePostRedrawHook = null, prevented = false, topModule, unloaders = []; + var roots = [], components = [], controllers = [], lastRedrawId = null, lastRedrawCallTime = 0, computePostRedrawHook = null, prevented = false, topComponent, unloaders = []; var FRAME_BUDGET = 16; //60 frames per second = 1 call per 16 ms - function submodule(module, args) { + function parameterize(component, args) { var controller = function() { - return (module.controller || function() {}).apply(this, args) || this + return (component.controller || function() {}).apply(this, args) || this } var view = function(ctrl) { if (arguments.length > 1) args = args.concat([].slice.call(arguments, 1)) - return module.view.apply(module, args ? [ctrl].concat(args) : [ctrl]) + return component.view.apply(component, args ? [ctrl].concat(args) : [ctrl]) } - controller.$original = module.controller + controller.$original = component.controller var output = {controller: controller, view: view} if (args[0] && args[0].key != null) output.attrs = {key: args[0].key} return output } - m.component = function(module) { - return function() { - return submodule(module, [].slice.call(arguments)) - } + m.component = function(component) { + return parameterize(component, [].slice.call(arguments, 1)) } - //m.module is deprecated, use m.mount - m.mount = m.module = function(root, module) { + m.mount = m.module = function(root, component) { if (!root) throw new Error("Please ensure the DOM element exists before rendering a template into it."); var index = roots.indexOf(root); if (index < 0) index = roots.length; @@ -588,14 +585,15 @@ var m = (function app(window, undefined) { m.redraw.strategy("all"); m.startComputation(); roots[index] = root; - var currentModule = topModule = module = module || {}; - var constructor = module.controller || function() {} + if (arguments.length > 2) component = subcomponent(component, [].slice.call(arguments, 2)) + var currentComponent = topComponent = component = component || {}; + var constructor = component.controller || function() {} var controller = new constructor; - //controllers may call m.module recursively (via m.route redirects, for example) - //this conditional ensures only the last recursive m.module call is applied - if (currentModule === topModule) { + //controllers may call m.mount recursively (via m.route redirects, for example) + //this conditional ensures only the last recursive m.mount call is applied + if (currentComponent === topComponent) { controllers[index] = controller; - modules[index] = module + components[index] = component } endFirstComputation(); return controllers[index] @@ -626,8 +624,8 @@ var m = (function app(window, undefined) { function redraw() { for (var i = 0, root; root = roots[i]; i++) { if (controllers[i]) { - var args = modules[i].controller && modules[i].controller.$$args ? [controllers[i]].concat(modules[i].controller.$$args) : [controllers[i]] - m.render(root, modules[i].view ? modules[i].view(controllers[i], args) : blank()) + var args = components[i].controller && components[i].controller.$$args ? [controllers[i]].concat(components[i].controller.$$args) : [controllers[i]] + m.render(root, components[i].view ? components[i].view(controllers[i], args) : blank()) } } //after rendering within a routed context, we need to scroll back to the top, and fetch the document title for history.pushState @@ -752,13 +750,13 @@ var m = (function app(window, undefined) { var keys = Object.keys(router); var index = keys.indexOf(path); if(index !== -1){ - m.module(root, router[keys [index]]); + m.mount(root, router[keys [index]]); return true; } for (var route in router) { if (route === path) { - m.module(root, router[route]); + m.mount(root, router[route]); return true } @@ -769,7 +767,7 @@ var m = (function app(window, undefined) { var keys = route.match(/:[^\/]+/g) || []; var values = [].slice.call(arguments, 1, -2); for (var i = 0, len = keys.length; i < len; i++) routeParams[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]) - m.module(root, router[route]) + m.mount(root, router[route]) }); return true } @@ -1140,5 +1138,5 @@ var m = (function app(window, undefined) { return m })(typeof window != "undefined" ? window : {}); -if (typeof module != "undefined" && module !== null && module.exports) module.exports = m; +if (typeof component != "undefined" && component !== null && component.exports) component.exports = m; else if (typeof define === "function" && define.amd) define(function() {return m}); diff --git a/modulator-test.html b/modulator-test.html new file mode 100644 index 00000000..761573ac --- /dev/null +++ b/modulator-test.html @@ -0,0 +1,274 @@ + + + + \ No newline at end of file diff --git a/tests/mithril-tests.js b/tests/mithril-tests.js index e381e83d..b29053ad 100644 --- a/tests/mithril-tests.js +++ b/tests/mithril-tests.js @@ -112,65 +112,48 @@ function testMithril(mock) { return unloaded }) test(function() { - //module should pass args to both controller and view + //component should pass args to both controller and view mock.requestAnimationFrame.$resolve() var root = mock.document.createElement("div") var slot1, slot2 - var module = m.component({ + var component = { controller: function(options) {slot1 = options.a}, view: function(ctrl, options) {slot2 = options.a} - }) - m.mount(root, module({a: 1})) + } + m.mount(root, m.component(component, {a: 1})) mock.requestAnimationFrame.$resolve() return slot1 == 1 && slot2 == 1 }) test(function() { - //module should work without controller + //component should work without controller mock.requestAnimationFrame.$resolve() var root = mock.document.createElement("div") var slot1, slot2 - var module = m.component({ + var component = { view: function(ctrl, options) {slot2 = options.a} - }) - m.mount(root, module({a: 1})) + } + m.mount(root, m.component(component, {a: 1})) mock.requestAnimationFrame.$resolve() return slot2 == 1 }) - test(function() { - //component(mod)(args) should also pass args to both controller and view - mock.requestAnimationFrame.$resolve() - - var root = mock.document.createElement("div") - var slot1, slot2 - var module = m.component({ - controller: function(options) {slot1 = options.a}, - view: function(ctrl, options) {slot2 = options.a} - }) - var moduleWithArgs = module({a: 1}) - m.mount(root, moduleWithArgs) - - mock.requestAnimationFrame.$resolve() - - return slot1 == 1 && slot2 == 1 - }) test(function() { //component controller should only run once mock.requestAnimationFrame.$resolve() var root = mock.document.createElement("div") var count1 = 0, count2 = 0 - var module = { + var component = { view: function(ctrl) { - return sub() + return sub } } - var sub = m.component({ + var sub = { controller: function() { count1++ }, @@ -178,8 +161,8 @@ function testMithril(mock) { count2++ return m("div", "test") } - }) - m.mount(root, module) + } + m.mount(root, component) mock.requestAnimationFrame.$resolve() @@ -195,12 +178,12 @@ function testMithril(mock) { var root = mock.document.createElement("div") var count1 = 0, count2 = 0, count3 = 0, count4 = 0 - var module = { + var component = { view: function(ctrl) { - return sub() + return sub } } - var sub = m.component({ + var sub = { controller: function() { count1++ }, @@ -208,7 +191,7 @@ function testMithril(mock) { count2++ return subsub } - }) + } var subsub = { controller: function() { count3++ @@ -218,7 +201,7 @@ function testMithril(mock) { return m("div", "test") } } - m.mount(root, module) + m.mount(root, component) mock.requestAnimationFrame.$resolve() @@ -234,21 +217,21 @@ function testMithril(mock) { var root = mock.document.createElement("div") var list = [1, 2, 3] - var module = { + var component = { controller: function() {}, view: function(ctrl) { return list.map(function(i) { - return sub({key: i}) + return m.component(sub, {key: i}) }) } } - var sub = m.component({ + var sub = { controller: function() {}, view: function() { return m("div") } - }) - m.mount(root, module) + } + m.mount(root, component) var firstBefore = root.childNodes[0] @@ -269,26 +252,26 @@ function testMithril(mock) { var root = mock.document.createElement("div") var list = [1, 2, 3] - var module = { + var component = { controller: function() {}, view: function(ctrl) { return list.map(function(i) { - return sub({key: i}) + return m.component(sub, {key: i}) }) } } - var sub = m.component({ + var sub = { view: function() { - return subsub() + return subsub } - }) - var subsub = m.component({ + } + var subsub = { controller: function() {}, view: function() { return m("div") } - }) - m.mount(root, module) + } + m.mount(root, component) var firstBefore = root.childNodes[0] @@ -309,21 +292,21 @@ function testMithril(mock) { var root = mock.document.createElement("div") var list = [1, 2, 3] - var module = { + var component = { controller: function() {}, view: function(ctrl) { return list.map(function(i) { - return sub({key: i}) + return m.component(sub, {key: i}) }) } } - var sub = m.component({ + var sub = { controller: function() {}, view: function() { return m("div", {key: 1}) } - }) - m.mount(root, module) + } + m.mount(root, component) var firstBefore = root.childNodes[0] @@ -344,27 +327,27 @@ function testMithril(mock) { var root = mock.document.createElement("div") var list = [1, 2, 3] - var module = { + var component = { controller: function() {}, view: function(ctrl) { return list.map(function(i) { - return sub({key: i}) + return m.component(sub, {key: i}) }) } } - var sub = m.component({ + var sub = { controller: function() {}, view: function() { return subsub } - }) + } var subsub = { controller: function() {}, view: function() { return m("div", {key: 1}) } } - m.mount(root, module) + m.mount(root, component) var firstBefore = root.childNodes[0] @@ -385,21 +368,21 @@ function testMithril(mock) { var root = mock.document.createElement("div") var list = [1, 2, 3] - var module = { + var component = { controller: function() {}, view: function(ctrl) { return list.map(function(i) { - return m("div", {key: i}, sub()) + return m("div", {key: i}, sub) }) } } - var sub = m.component({ + var sub = { controller: function() {}, view: function() { return m("div") } - }) - m.mount(root, module) + } + m.mount(root, component) var firstBefore = root.childNodes[0].childNodes[0] @@ -420,27 +403,27 @@ function testMithril(mock) { var root = mock.document.createElement("div") var list = [1, 2, 3] - var module = { + var component = { controller: function() {}, view: function(ctrl) { return list.map(function(i) { - return m("div", {key: i}, sub()) + return m("div", {key: i}, sub) }) } } - var sub = m.component({ + var sub = { controller: function() {}, view: function() { return subsub } - }) + } var subsub = { controller: function() {}, view: function() { return m("div") } } - m.mount(root, module) + m.mount(root, component) var firstBefore = root.childNodes[0].childNodes[0] @@ -462,15 +445,15 @@ function testMithril(mock) { var root = mock.document.createElement("div") var list = [1, 2, 3] var unloaded - var module = { + var component = { controller: function() {}, view: function(ctrl) { return list.map(function(i) { - return sub({key: i}) + return m.component(sub, {key: i}) }) } } - var sub = m.component({ + var sub = { controller: function(opts) { this.onunload = function() { unloaded = opts.key @@ -479,8 +462,8 @@ function testMithril(mock) { view: function() { return m("div") } - }) - m.mount(root, module) + } + m.mount(root, component) var firstBefore = root.childNodes[0] @@ -500,25 +483,25 @@ function testMithril(mock) { var root = mock.document.createElement("div") var list = [1, 2, 3] var unloaded1, unloaded2 - var module = { + var component = { controller: function() {}, view: function(ctrl) { return list.map(function(i) { - return sub({key: i}) + return m.component(sub, {key: i}) }) } } - var sub = m.component({ + var sub = { controller: function(opts) { this.onunload = function() { unloaded1 = opts.key } }, view: function(ctrl, opts) { - return subsub({key: opts.key}) + return m.component(subsub, {key: opts.key}) } - }) - var subsub = m.component({ + } + var subsub = { controller: function(opts) { this.onunload = function() { unloaded2 = opts.key @@ -527,8 +510,8 @@ function testMithril(mock) { view: function() { return m("div") } - }) - m.mount(root, module) + } + m.mount(root, component) var firstBefore = root.childNodes[0] @@ -547,13 +530,13 @@ function testMithril(mock) { var root = mock.document.createElement("div") var count = 0 - var module = { + var component = { controller: function() {}, view: function(ctrl) { - return sub() + return sub } } - var sub = m.component({ + var sub = { controller: function(opts) { m.redraw() }, @@ -561,8 +544,8 @@ function testMithril(mock) { count++ return m("div") } - }) - m.mount(root, module) + } + m.mount(root, component) mock.requestAnimationFrame.$resolve() @@ -574,18 +557,18 @@ function testMithril(mock) { var root = mock.document.createElement("div") var count = 0 - var module = { + var component = { controller: function() {}, view: function(ctrl) { - return sub() + return sub } } - var sub = m.component({ + var sub = { controller: function(opts) {}, view: function() { return subsub } - }) + } var subsub = { controller: function(opts) { m.redraw() @@ -595,7 +578,7 @@ function testMithril(mock) { return m("div") } } - m.mount(root, module) + m.mount(root, component) mock.requestAnimationFrame.$resolve() @@ -609,13 +592,13 @@ function testMithril(mock) { var root = mock.document.createElement("div") var loaded = false var testEnabled = true - var module = { + var component = { controller: function() {}, view: function() { - return sub() + return sub } } - var sub = m.component({ + var sub = { controller: function(opts) { controller = this this.onunload = function(e) {if (testEnabled) e.preventDefault()} @@ -623,9 +606,9 @@ function testMithril(mock) { view: function() { return m("div") } - }) + } m.route(root, "/a", { - "/a": module, + "/a": component, "/b": {controller: function() {loaded = true}, view: function() {}} }) @@ -646,20 +629,20 @@ function testMithril(mock) { var root = mock.document.createElement("div") var loaded = false var testEnabled = true - var module = { + var component = { controller: function() {}, view: function() { - return sub() + return sub } } - var sub = m.component({ + var sub = { controller: function(opts) { }, view: function() { - return subsub() + return subsub } - }) - var subsub = m.component({ + } + var subsub = { controller: function(opts) { controller = this this.onunload = function(e) {if (testEnabled) e.preventDefault()} @@ -667,9 +650,9 @@ function testMithril(mock) { view: function() { return m("div") } - }) + } m.route(root, "/a", { - "/a": module, + "/a": component, "/b": {controller: function() {loaded = true}, view: function() {}} }) @@ -690,7 +673,7 @@ function testMithril(mock) { var root = mock.document.createElement("div") var loaded = false var testEnabled = true - var module = { + var component = { controller: function() {}, view: function() { return sub @@ -706,7 +689,7 @@ function testMithril(mock) { } } m.route(root, "/a", { - "/a": module, + "/a": component, "/b": {controller: function() {loaded = true}, view: function() {}} }) @@ -727,7 +710,7 @@ function testMithril(mock) { var root = mock.document.createElement("div") var loaded = false var testEnabled = true - var module = { + var component = { controller: function() {}, view: function() { return sub @@ -749,7 +732,7 @@ function testMithril(mock) { } } m.route(root, "/a", { - "/a": module, + "/a": component, "/b": {controller: function() {loaded = true}, view: function() {}} }) @@ -763,7 +746,7 @@ function testMithril(mock) { return loaded === false }) test(function() { - // nested modules under keyed components should render + // nested components under keyed components should render mock.requestAnimationFrame.$resolve() var root = mock.document.createElement("div") @@ -772,27 +755,27 @@ function testMithril(mock) { controller: function() {}, view: function(ctrl) { return m('.outer', [ - m('.inner', CommentList({ list: [1, 2, 3] })) + m('.inner', m.component(CommentList, { list: [1, 2, 3] })) ]) } } - var CommentList = m.component({ + var CommentList = { controller: function() {}, view: function(ctrl, props) { return m('.list', props.list.map(function(i) { return m('.comment', [ - Reply({key: i}) + m.component(Reply, {key: i}) ]) })) } - }) - var Reply = m.component({ + } + var Reply = { controller: function() {}, view: function() { count++ return m(".reply") } - }) + } m.mount(root, App) mock.requestAnimationFrame.$resolve() @@ -807,26 +790,26 @@ function testMithril(mock) { var root = mock.document.createElement("div") var countA = 0 var countB = 0 - var subA = m.component({ + var subA = { controller: function(){ countA += 1 }, view: function() { return m("div") } - }) - var subB = m.component({ + } + var subB = { controller: function() { countB += 1 }, view: function() { return m("div") } - }) + } m.route(root, "/a", { "/a": { view: function () { return m('.page-a', [ - m('h1'), subA({ x: 11 }) + m('h1'), m.component(subA, { x: 11 }) ]) } }, "/b": { view: function() { return m('.page-b', [ - m('h2'), subB({ y: 22 }) + m('h2'), m.component(subB, { y: 22 }) ]) } } @@ -846,12 +829,12 @@ function testMithril(mock) { }) test(function() { var root = mock.document.createElement("div") - var module = {}, unloaded = false - module.controller = function() { + var component = {}, unloaded = false + component.controller = function() { this.onunload = function() {unloaded = true} } - module.view = function() {} - m.mount(root, module) + component.view = function() {} + m.mount(root, component) m.mount(root, {controller: function() {}, view: function() {}}) return unloaded === true @@ -861,13 +844,13 @@ function testMithril(mock) { var root = mock.document.createElement("div") var initCount = 0 - var module = {} - module.view = function() { + var component = {} + component.view = function() { return m("div", {config: function(el, init) { if (!init) initCount++ }}) } - m.mount(root, module) + m.mount(root, component) mock.requestAnimationFrame.$resolve() @@ -884,7 +867,7 @@ function testMithril(mock) { var show = true - var module = { + var component = { view: function() { return [ m(".foo", {key: 1, config: test, onclick: function() {show = !show}}), @@ -899,7 +882,7 @@ function testMithril(mock) { } } - m.mount(root, module) + m.mount(root, component) mock.requestAnimationFrame.$resolve() @@ -952,6 +935,31 @@ function testMithril(mock) { return root.childNodes.length == 2 }) + test(function() { + // Components should not require a view + mock.requestAnimationFrame.$resolve() + mock.location.search = "?" + + var Component = { + view: function () { + return m('.comp') + } + } + + var root = mock.document.createElement("div") + m.route.mode = "search" + m.route(root, "/foo", { + "/foo": { + view: function() { + return [ Component ] + } + } + }) + + mock.requestAnimationFrame.$resolve() + + return root.childNodes[0].nodeName == "DIV" + }) m.redraw.strategy(undefined) //teardown for m.mount tests //m.withAttr @@ -1890,13 +1898,13 @@ function testMithril(mock) { mock.requestAnimationFrame.$resolve() //setup mock.location.search = "?" - var module = {controller: function() {}, view: function() {return m.route.param("test")}} + var component = {controller: function() {}, view: function() {return m.route.param("test")}} var root = mock.document.createElement("div") m.route.mode = "search" m.route(root, "/test5/foo", { - "/": module, - "/test5/:test": module + "/": component, + "/test5/:test": component }) var paramValueBefore = m.route.param("test") mock.requestAnimationFrame.$resolve() @@ -1909,13 +1917,13 @@ function testMithril(mock) { mock.requestAnimationFrame.$resolve() //setup mock.location.search = "?" - var module = {controller: function() {}, view: function() {return m.route.param("a1")}} + var component = {controller: function() {}, view: function() {return m.route.param("a1")}} var root = mock.document.createElement("div") m.route.mode = "search" m.route(root, "/test6/foo", { - "/": module, - "/test6/:a1": module + "/": component, + "/test6/:a1": component }) var paramValueBefore = m.route.param("a1") mock.requestAnimationFrame.$resolve() @@ -1929,13 +1937,13 @@ function testMithril(mock) { mock.requestAnimationFrame.$resolve() //setup mock.location.search = "?" - var module = {controller: function() {}, view: function() {return m.route.param("a1")}} + var component = {controller: function() {}, view: function() {return m.route.param("a1")}} var root = mock.document.createElement("div") m.route.mode = "search" m.route(root, "/test7/foo", { - "/": module, - "/test7/:a1": module + "/": component, + "/test7/:a1": component }) var routeValueBefore = m.route() mock.requestAnimationFrame.$resolve()