Skip to content

Commit 9e8dc69

Browse files
committed
rework api
1 parent 6abb868 commit 9e8dc69

14 files changed

+827
-342
lines changed

docs/components.md

+71-71
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Here, we've defined a class called `Contact`. A contact has an id, a name and an
4141
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.
4242

4343
```javascript
44-
var ContactsWidget = m.component({
44+
var ContactsWidget = {
4545
controller: function update() {
4646
this.contacts = Contact.list()
4747
this.save = function(contact) {
@@ -50,13 +50,13 @@ var ContactsWidget = m.component({
5050
},
5151
view: function(ctrl) {
5252
return [
53-
ContactForm({onsave: ctrl.save}),
54-
ContactList({contacts: ctrl.contacts})
53+
m.component(ContactForm, {onsave: ctrl.save}),
54+
m.component(ContactList, {contacts: ctrl.contacts})
5555
]
5656
}
57-
})
57+
}
5858

59-
var ContactForm = m.component({
59+
var ContactForm = {
6060
controller: function(args) {
6161
this.contact = m.prop(args.contact || new Contact())
6262
},
@@ -73,9 +73,9 @@ var ContactForm = m.component({
7373
m("button[type=button]", {onclick: args.onsave.bind(this, contact)}, "Save")
7474
])
7575
}
76-
})
76+
}
7777

78-
var ContactList = m.component({
78+
var ContactList = {
7979
view: function(ctrl, args) {
8080
return m("table", [
8181
args.contacts().map(function(contact) {
@@ -87,9 +87,9 @@ var ContactList = m.component({
8787
})
8888
])
8989
}
90-
})
90+
}
9191

92-
m.module(document.body, ContactsWidget())
92+
m.mount(document.body, ContactsWidget)
9393
```
9494

9595
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
123123
Here's a refactored version of the sample app above to illustrate:
124124

125125
```javascript
126-
var ContactForm = m.component({
126+
var ContactForm = {
127127
controller: function() {
128128
this.contact = m.prop(new Contact())
129129
this.save = function(contact) {
@@ -143,7 +143,7 @@ var ContactForm = m.component({
143143
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
144144
])
145145
}
146-
})
146+
}
147147

148148
var ContactList = {
149149
controller: function() {
@@ -163,8 +163,8 @@ var ContactList = {
163163
}
164164

165165
m.route(document.body, "/", {
166-
"/list": ContactList(),
167-
"/create": ContactForm()
166+
"/list": ContactList,
167+
"/create": ContactForm
168168
})
169169
```
170170

@@ -200,16 +200,16 @@ var Observable = function() {
200200
}.call()
201201

202202

203-
var ContactsWidget = m.component({
203+
var ContactsWidget = {
204204
view: function(ctrl) {
205205
return [
206-
m.module(ContactForm),
207-
m.module(ContactList)
206+
ContactForm,
207+
ContactList
208208
]
209209
}
210-
})
210+
}
211211

212-
var ContactForm = m.component({
212+
var ContactForm = {
213213
controller: function() {
214214
this.contact = m.prop(new Contact())
215215
this.save = function(contact) {
@@ -229,9 +229,9 @@ var ContactForm = m.component({
229229
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
230230
])
231231
}
232-
})
232+
}
233233

234-
var ContactList = m.component({
234+
var ContactList = {
235235
controller: Observable.register(function() {
236236
this.contacts = Contact.list()
237237
}),
@@ -246,9 +246,9 @@ var ContactList = m.component({
246246
})
247247
])
248248
}
249-
})
249+
}
250250

251-
m.module(document.body, ContactsWidget())
251+
m.module(document.body, ContactsWidget)
252252
```
253253

254254
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
311311
The example below shows a variation of the contacts app where `ContactForm` is responsible for saving.
312312

313313
```javascript
314-
var ContactsWidget = m.component({
314+
var ContactsWidget = {
315315
controller: Observable.register(["updateContact"], function() {
316316
this.contacts = Contact.list()
317317
}),
318318
view: function(ctrl) {
319319
return [
320-
ContactForm(),
321-
ContactList({contacts: ctrl.contacts})
320+
m.component(ContactForm),
321+
m.component(ContactList, {contacts: ctrl.contacts})
322322
]
323323
}
324-
})
324+
}
325325

326-
var ContactForm = m.component({
326+
var ContactForm = {
327327
controller: function(args) {
328328
this.contact = m.prop(new Contact())
329329
this.save = function(contact) {
@@ -343,9 +343,9 @@ var ContactForm = m.component({
343343
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
344344
])
345345
}
346-
})
346+
}
347347

348-
var ContactList = m.component({
348+
var ContactList = {
349349
view: function(ctrl, args) {
350350
return m("table", [
351351
args.contacts().map(function(contact) {
@@ -357,9 +357,9 @@ var ContactList = m.component({
357357
})
358358
])
359359
}
360-
})
360+
}
361361

362-
m.mount(document.body, ContactsWidget())
362+
m.mount(document.body, ContactsWidget)
363363
```
364364

365365
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) {
379379
})
380380

381381
//ContactsWidget is the same as before
382-
var ContactsWidget = m.component({
382+
var ContactsWidget = {
383383
controller: Observable.register(["updateContact"], function() {
384384
this.contacts = Contact.list()
385385
}),
386386
view: function(ctrl) {
387387
return [
388-
ContactForm(),
389-
ContactList({contacts: ctrl.contacts})
388+
m.component(ContactForm),
389+
m.component(ContactList, {contacts: ctrl.contacts})
390390
]
391391
}
392-
})
392+
}
393393

394394
//ContactList no longer calls `Contact.save`
395-
var ContactForm = m.component({
395+
var ContactForm = {
396396
controller: function(args) {
397397
this.contact = m.prop(new Contact())
398398
this.save = function(contact) {
@@ -412,10 +412,10 @@ var ContactForm = m.component({
412412
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
413413
])
414414
}
415-
})
415+
}
416416

417417
//ContactList is the same as before
418-
var ContactList = m.component({
418+
var ContactList = {
419419
view: function(ctrl, args) {
420420
return m("table", [
421421
args.contacts().map(function(contact) {
@@ -427,9 +427,9 @@ var ContactList = m.component({
427427
})
428428
])
429429
}
430-
})
430+
}
431431

432-
m.mount(document.body, ContactsWidget())
432+
m.mount(document.body, ContactsWidget)
433433
```
434434

435435
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
445445
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.
446446

447447
```javascript
448-
var Uploader = m.component({
448+
var Uploader = {
449+
upload: function(options) {
450+
var formData = new FormData
451+
for (var key in options.data) {
452+
for (var i = 0; i < options.data[key].length; i++) {
453+
formData.append(key, files[i])
454+
}
455+
}
456+
457+
//simply pass the FormData object intact to the underlying XMLHttpRequest, instead of JSON.stringify'ing it
458+
options.serialize = function(value) {return value}
459+
options.data = formData
460+
461+
return m.request(options)
462+
},
463+
serialize: function(files) {
464+
var promises = Array.prototype.slice.call(files).map(function(file) {
465+
var deferred = m.deferred()
466+
467+
var reader = new FileReader
468+
reader.readAsDataURL()
469+
reader.onloadend = function(e) {
470+
deferred.resolve(e.result)
471+
}
472+
reader.onerror = deferred.reject
473+
return deferred
474+
})
475+
return m.sync(promises)
476+
},
449477
controller: function(args) {
450478
this.noop = function(e) {
451479
e.preventDefault()
@@ -460,34 +488,6 @@ var Uploader = m.component({
460488
view: function(ctrl, args) {
461489
return m(".uploader", {ondragover: ctrl.noop, ondrop: ctrl.update})
462490
}
463-
})
464-
Uploader.upload = function(options) {
465-
var formData = new FormData
466-
for (var key in options.data) {
467-
for (var i = 0; i < options.data[key].length; i++) {
468-
formData.append(key, files[i])
469-
}
470-
}
471-
472-
//simply pass the FormData object intact to the underlying XMLHttpRequest, instead of JSON.stringify'ing it
473-
options.serialize = function(value) {return value}
474-
options.data = formData
475-
476-
return m.request(options)
477-
}
478-
Uploader.serialize = function(files) {
479-
var promises = Array.prototype.slice.call(files).map(function(file) {
480-
var deferred = m.deferred()
481-
482-
var reader = new FileReader
483-
reader.readAsDataURL()
484-
reader.onloadend = function(e) {
485-
deferred.resolve(e.result)
486-
}
487-
reader.onerror = deferred.reject
488-
return deferred
489-
})
490-
return m.sync(promises)
491491
}
492492
```
493493

@@ -506,7 +506,7 @@ var Demo1 = {
506506
view: function(ctrl) {
507507
return [
508508
m("h1", "Uploader demo"),
509-
Uploader({onchange: ctrl.upload})
509+
m.component(Uploader, {onchange: ctrl.upload})
510510
]
511511
}
512512
}
@@ -534,7 +534,7 @@ var Demo2 = {
534534
return [
535535
m("h1", "Uploader demo"),
536536
m("form", [
537-
Uploader({onchange: ctrl.files}),
537+
m.component(Uploader, {onchange: ctrl.files}),
538538
m("button[type=button]", {onclick: ctrl.save})
539539
])
540540
]

docs/integration.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The example below shows a simple component that integrates with the [select2 lib
88

99
```javascript
1010
//Select2 component (assumes both jQuery and Select2 are included in the page)
11-
var Select2 = m.component({
11+
var Select2 = {
1212
//this view implements select2's `<select>` progressive enhancement mode
1313
view: function(ctrl) {
1414
return m("select", {config: select2.config(ctrl)}, [
@@ -17,7 +17,7 @@ var Select2 = m.component({
1717
})
1818
]);
1919
}
20-
});
20+
};
2121

2222
/**
2323
Select2 config factory. The params in this doc refer to properties of the `ctrl` argument
@@ -56,7 +56,7 @@ Select2.config = function(ctrl) {
5656

5757

5858
//usage
59-
var Dashboard = m.component({
59+
var Dashboard = {
6060
controller: function() {
6161
//list of users to show
6262
var data = [{id: 1, name: "John"}, {id: 2, name: "Mary"}, {id: 3, name: "Jane"}]
@@ -76,10 +76,10 @@ var Dashboard = m.component({
7676
view: function(ctrl) {
7777
return m("div", [
7878
m("label", "User:"),
79-
Select2({data: ctrl.data, value: ctrl.currentUser.id, onchange: ctrl.changeUser})
79+
m.component(Select2, {data: ctrl.data, value: ctrl.currentUser.id, onchange: ctrl.changeUser})
8080
]);
8181
}
82-
});
82+
};
8383

8484

8585
m.mount(document.body, Dashboard);

docs/layout/api.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ <h2 id="api">API (v$version)</h2>
2626
<h3 id="core">Core</h3>
2727
<ul>
2828
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
29-
<li><a href="mithril.component.html" title="Creates a component">m.component</a></li>
29+
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
3030
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
3131
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
3232
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>

0 commit comments

Comments
 (0)