allow multiple VMs sharing one data object

This commit is contained in:
Evan You 2013-08-21 13:32:33 -04:00
parent 5b96bdc556
commit 75a4850afb
3 changed files with 69 additions and 38 deletions

View File

@ -6,18 +6,26 @@
<script src="../dist/seed.js"></script>
</head>
<body>
<h1>a.b.c : <span sd-text="a.b.c"></span></h1>
<h2>a.c : <span sd-text="a.c"></span></h2>
<h3>Computed property that concats the two: <span sd-text="d"></span></h3>
<button sd-on="click:one">one</button>
<button sd-on="click:two">two</button>
<button sd-on="click:three">three</button>
<p><input sd-value="msg"></p>
<div id="a">
<h1>a.b.c : <span sd-text="a.b.c"></span></h1>
<h2>a.c : <span sd-text="a.c"></span></h2>
<h3>Computed property that concats the two: <span sd-text="d"></span></h3>
<button sd-on="click:one">one</button>
<button sd-on="click:two">two</button>
<button sd-on="click:three">three</button>
<p><input sd-value="msg"></p>
</div>
<div id="b">
<h1 sd-text="a.c"></h1>
</div>
<script>
seed.config({debug: true})
var data = { c: 555 }
var Demo = seed.ViewModel.extend({
init: function () {
this.msg = 'Yoyoyo' },
this.msg = 'Yoyoyo'
this.a = data
},
props: {
one: function () {
this.a = {
@ -42,7 +50,13 @@
}}
}
})
var app = new Demo({ el: document.body })
var app = new Demo({ el: '#a' }),
app2 = new seed.ViewModel({
el: '#b',
data: {
a: data
}
})
</script>
</body>
</html>

View File

@ -5,12 +5,10 @@ var Emitter = require('emitter'),
Binding = require('./binding'),
DirectiveParser = require('./directive-parser'),
TextParser = require('./text-parser'),
DepsParser = require('./deps-parser')
var slice = Array.prototype.slice
// late bindings
var vmAttr, eachAttr
DepsParser = require('./deps-parser'),
slice = Array.prototype.slice,
vmAttr,
eachAttr
/*
* The DOM compiler
@ -59,17 +57,19 @@ function Compiler (vm, options) {
utils.log('\nnew VM instance: ', el, '\n')
// set el
vm.$el = el
// link it up!
// set stuff on the ViewModel
vm.$el = el
vm.$compiler = this
vm.$parent = options.parentCompiler && options.parentCompiler.vm
// now for the compiler itself...
this.vm = vm
this.el = vm.$el
this.directives = []
this.vm = vm
this.el = el
this.directives = []
// Store things during parsing to be processed afterwards,
// because we want to have created all bindings before
// observing values / parsing dependencies.
var observables = this.observables = []
var computed = this.computed = [] // computed props to parse deps from
var ctxBindings = this.contextBindings = [] // computed props with dynamic context
@ -86,25 +86,28 @@ function Compiler (vm, options) {
// setup observer
this.setupObserver()
// call user init
// call user init. this will capture some initial values.
if (options.init) {
options.init.apply(vm, options.args || [])
}
// now parse the DOM
// now parse the DOM, during which we will create necessary bindings
// and bind the parsed directives
this.compileNode(this.el, true)
// for anything in viewmodel but not binded in DOM, create bindings for them
// for anything in viewmodel but not binded in DOM, also create bindings for them
for (key in vm) {
if (vm.hasOwnProperty(key) &&
key.charAt(0) !== '$' &&
!this.bindings[key])
!this.bindings.hasOwnProperty(key))
{
this.createBinding(key)
}
}
// observe root keys
// observe root values so that they emit events when
// their nested values change (for an Object)
// or when they mutate (for an Array)
var i = observables.length, binding
while (i--) {
binding = observables[i]
@ -112,11 +115,10 @@ function Compiler (vm, options) {
}
// extract dependencies for computed properties
if (computed.length) DepsParser.parse(computed)
this.computed = null
// extract dependencies for computed properties with dynamic context
if (ctxBindings.length) this.bindContexts(ctxBindings)
this.contextBindings = null
this.observables = this.computed = this.contextBindings = null
}
var CompilerProto = Compiler.prototype
@ -129,7 +131,8 @@ var CompilerProto = Compiler.prototype
CompilerProto.setupObserver = function () {
var bindings = this.bindings,
observer = this.observer = new Emitter()
observer = this.observer = new Emitter(),
depsOb = DepsParser.observer
// a hash to hold event proxies for each root level key
// so they can be referenced and removed later
@ -138,15 +141,15 @@ CompilerProto.setupObserver = function () {
// add own listeners which trigger binding updates
observer
.on('get', function (key) {
if (DepsParser.observer.isObserving) {
DepsParser.observer.emit('get', bindings[key])
if (depsOb.isObserving && bindings[key]) {
depsOb.emit('get', bindings[key])
}
})
.on('set', function (key, val) {
bindings[key].update(val)
if (bindings[key]) bindings[key].update(val)
})
.on('mutate', function (key) {
bindings[key].pub()
if (bindings[key]) bindings[key].pub()
})
}
@ -406,11 +409,13 @@ CompilerProto.define = function (key, binding) {
binding.value.set(value)
}
} else if (value !== binding.value) {
// unwatch the old value
Observer.unobserve(binding.value, key, compiler.observer)
// set new value
binding.value = value
compiler.observer.emit('set', key, value)
// unwatch the old value!
Observer.unobserve(binding.value, key, compiler.observer)
// now watch the new one instead
// now watch the new value, which in turn emits 'set'
// for all its nested values
Observer.observe(value, key, compiler.observer)
}
}

View File

@ -58,6 +58,9 @@ function bind (obj, key, path, observer) {
values = obj.__values__,
fullKey = (path ? path + '.' : '') + key
values[fullKey] = val
// emit set on bind
// this means when an object is observed it will emit
// a first batch of set events.
observer.emit('set', fullKey, val)
def(obj, key, {
enumerable: true,
@ -89,6 +92,13 @@ function isWatchable (obj) {
return type === 'Object' || type === 'Array'
}
function emitSet (obj, observer) {
var values = obj.__values__
for (var key in values) {
observer.emit('set', key, values[key])
}
}
module.exports = {
observe: function (obj, path, observer) {
@ -114,7 +124,9 @@ module.exports = {
.on('get', proxies.get)
.on('set', proxies.set)
.on('mutate', proxies.mutate)
if (!alreadyConverted) {
if (alreadyConverted) {
emitSet(obj, obj.__observer__)
} else {
watch(obj, null, ob)
}
}