fix observer code example
This commit is contained in:
parent
5011dd97b6
commit
4632a61959
1 changed files with 40 additions and 32 deletions
|
|
@ -348,7 +348,7 @@ var ContactForm = {
|
||||||
m("label", "Email"),
|
m("label", "Email"),
|
||||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||||
|
|
||||||
m("button", {onclick: args.onsave.bind(this, contact)}, "Save")
|
m("button[type=button]", {onclick: args.onsave.bind(this, contact)}, "Save")
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -418,7 +418,7 @@ var ContactForm = {
|
||||||
m("label", "Email"),
|
m("label", "Email"),
|
||||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||||
|
|
||||||
m("button", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -468,7 +468,7 @@ var Observable = function() {
|
||||||
return ctrl
|
return ctrl
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update: function() {
|
trigger: function() {
|
||||||
controllers.map(function(c) {
|
controllers.map(function(c) {
|
||||||
ctrl = new c.controller
|
ctrl = new c.controller
|
||||||
for (var i in ctrl) c.instance[i] = ctrl[i]
|
for (var i in ctrl) c.instance[i] = ctrl[i]
|
||||||
|
|
@ -491,7 +491,7 @@ var ContactForm = {
|
||||||
controller: function() {
|
controller: function() {
|
||||||
this.contact = m.prop(new Contact())
|
this.contact = m.prop(new Contact())
|
||||||
this.save = function(contact) {
|
this.save = function(contact) {
|
||||||
Contact.save(contact).then(Observable.update)
|
Contact.save(contact).then(Observable.trigger)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
view: function() {
|
view: function() {
|
||||||
|
|
@ -504,7 +504,7 @@ var ContactForm = {
|
||||||
m("label", "Email"),
|
m("label", "Email"),
|
||||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||||
|
|
||||||
m("button", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -531,9 +531,9 @@ 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.
|
In this iteration, both the `ContactForm` and `ContactList` components are now children of the `ContactsWidget` component and they appear simultaneously on the same page.
|
||||||
|
|
||||||
The `Observable` object exposes two methods: `register` which marks a controller as a Observable entity, and `update` which reloads controllers marked by `register`. Controllers are deregistered when their `onunload` event is triggered.
|
The `Observable` object exposes two methods: `register` which marks a controller as a Observable entity, and `trigger` which reloads controllers marked by `register`. Controllers are deregistered when their `onunload` event is triggered.
|
||||||
|
|
||||||
The `ContactList` component's controller is marked as Observable, and the `save` event handler in `ContactForm` calls `Observable.update` after saving.
|
The `ContactList` component's controller is marked as Observable, and the `save` event handler in `ContactForm` calls `Observable.trigger` after saving.
|
||||||
|
|
||||||
This mechanism allows multiple components to be reloaded in response to non-idempotent operations.
|
This mechanism allows multiple components to be reloaded in response to non-idempotent operations.
|
||||||
|
|
||||||
|
|
@ -541,31 +541,39 @@ One extremely important aspect of this architecture is that since components enc
|
||||||
|
|
||||||
### The observer pattern
|
### The observer pattern
|
||||||
|
|
||||||
The `Observable` object can be further refactored so that `update` broadcasts to "channels", which controllers can subscribe to. This is known, appropriately, as the [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern).
|
The `Observable` object can be further refactored so that `trigger` broadcasts to "channels", which controllers can subscribe to. This is known, appropriately, as the [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern).
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var Observable = function() {
|
var Observable = function() {
|
||||||
var channels = {}
|
var channels = {}
|
||||||
return {
|
return {
|
||||||
register: function(subcriptions, controller) {
|
register: function(subscriptions, controller) {
|
||||||
return function() {
|
return function self() {
|
||||||
var ctrl = new controller
|
var ctrl = new controller
|
||||||
|
var reload = controller.bind(ctrl)
|
||||||
|
Observable.on(subscriptions, reload)
|
||||||
ctrl.onunload = function() {
|
ctrl.onunload = function() {
|
||||||
subscriptions.forEach(function(subscription) {
|
Observable.off(reload)
|
||||||
channels[subscription].splice(controllers.indexOf(ctrl), 1)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
subscriptions.forEach(function(subscription) {
|
|
||||||
if (!channels[subscription]) channels[subscription] = []
|
|
||||||
channels[subscription].push({instance: ctrl, controller: controller})
|
|
||||||
})
|
|
||||||
return ctrl
|
return ctrl
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
broadcast: function(channel, args) {
|
on: function(subscriptions, callback) {
|
||||||
channels[channel].map(function(c) {
|
subscriptions.forEach(function(subscription) {
|
||||||
ctrl = new c.controller(args)
|
if (!channels[subscription]) channels[subscription] = []
|
||||||
for (var i in ctrl) c.instance[i] = ctrl[i]
|
channels[subscription].push(callback)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
off: function(callback) {
|
||||||
|
for (var channel in channels) {
|
||||||
|
var index = channels[channel].indexOf(callback)
|
||||||
|
if (index > -1) channels[channel].splice(index, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: function(channel, args) {
|
||||||
|
console.log(channel)
|
||||||
|
channels[channel].map(function(callback) {
|
||||||
|
callback(args)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -588,16 +596,16 @@ var ContactsWidget = {
|
||||||
view: function(ctrl) {
|
view: function(ctrl) {
|
||||||
return [
|
return [
|
||||||
m.module(ContactForm),
|
m.module(ContactForm),
|
||||||
m.module(ContactList, {contacts: contacts})
|
m.module(ContactList, {contacts: ctrl.contacts})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ContactForm = {
|
var ContactForm = {
|
||||||
controller: function(args) {
|
controller: function(args) {
|
||||||
this.contact = m.prop(args.contact || new Contact())
|
this.contact = m.prop(new Contact())
|
||||||
this.save = function() {
|
this.save = function(contact) {
|
||||||
Contact.save(contact).then(Observable.broadcast("updateContact"))
|
Contact.save(contact).then(Observable.trigger("updateContact"))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
view: function(ctrl, args) {
|
view: function(ctrl, args) {
|
||||||
|
|
@ -610,7 +618,7 @@ var ContactForm = {
|
||||||
m("label", "Email"),
|
m("label", "Email"),
|
||||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||||
|
|
||||||
m("button", {onclick: args.onsave.bind(this, contact)}, "Save")
|
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -644,8 +652,8 @@ Here's one last, but relevant variation of the pattern above.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
//model layer observer
|
//model layer observer
|
||||||
Observable.register(["saveContact"], function(data) {
|
Observable.on(["saveContact"], function(data) {
|
||||||
Contact.save(data.contact).then(Observable.broadcast("updateContact"))
|
Contact.save(data.contact).then(Observable.trigger("updateContact"))
|
||||||
})
|
})
|
||||||
|
|
||||||
//ContactsWidget is the same as before
|
//ContactsWidget is the same as before
|
||||||
|
|
@ -656,7 +664,7 @@ var ContactsWidget = {
|
||||||
view: function(ctrl) {
|
view: function(ctrl) {
|
||||||
return [
|
return [
|
||||||
m.module(ContactForm),
|
m.module(ContactForm),
|
||||||
m.module(ContactList, {contacts: contacts})
|
m.module(ContactList, {contacts: ctrl.contacts})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -664,9 +672,9 @@ var ContactsWidget = {
|
||||||
//ContactList no longer calls `Contact.save`
|
//ContactList no longer calls `Contact.save`
|
||||||
var ContactForm = {
|
var ContactForm = {
|
||||||
controller: function(args) {
|
controller: function(args) {
|
||||||
this.contact = m.prop(args.contact || new Contact())
|
this.contact = m.prop(new Contact())
|
||||||
this.save = function(contact) {
|
this.save = function(contact) {
|
||||||
Observable.broadcast("updateContact", {contact: contact})
|
Observable.trigger("saveContact", {contact: contact})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
view: function(ctrl, args) {
|
view: function(ctrl, args) {
|
||||||
|
|
@ -679,7 +687,7 @@ var ContactForm = {
|
||||||
m("label", "Email"),
|
m("label", "Email"),
|
||||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||||
|
|
||||||
m("button", {onclick: args.onsave.bind(this, contact)}, "Save")
|
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue