mirror of https://github.com/vuejs/vue.git
fix each.js for new architecture
This commit is contained in:
parent
77ce1fc355
commit
c6c5fdb3d8
5
TODO.md
5
TODO.md
|
|
@ -1,8 +1,5 @@
|
|||
- fix architecture: objects as single source of truth...
|
||||
- auto add .length binding for Arrays
|
||||
- $watch / $unwatch & $destroy(now much easier)
|
||||
- $watch / $unwatch (now much easier)
|
||||
- add a few util methods, e.g. extend, inherits, traverse
|
||||
- prototypal scope/binding inheritance (Object.create)
|
||||
- tests
|
||||
- docs
|
||||
- sd-with?
|
||||
|
|
|
|||
|
|
@ -10,6 +10,17 @@
|
|||
<li sd-each="item:items" sd-text="item.title"></li>
|
||||
</ul>
|
||||
<p>Total items: {{items.length}}</p>
|
||||
<p>
|
||||
<button sd-on="click:push">push</button>
|
||||
<button sd-on="click:pop">pop</button>
|
||||
<button sd-on="click:shift">shift</button>
|
||||
<button sd-on="click:unshift">unshift</button>
|
||||
<button sd-on="click:splice">splice</button>
|
||||
<button sd-on="click:remove">remove</button>
|
||||
<button sd-on="click:replace">replace</button>
|
||||
<button sd-on="click:sort">sort</button>
|
||||
<button sd-on="click:reverse">reverse</button>
|
||||
</p>
|
||||
</div>
|
||||
<script src="../dist/seed.js"></script>
|
||||
<script>
|
||||
|
|
@ -17,24 +28,51 @@
|
|||
seed.config({debug: true})
|
||||
|
||||
var items = [
|
||||
{ title: 'hi' },
|
||||
{ title: 'ha' },
|
||||
{ title: 'hu' },
|
||||
{ title: 'ho' },
|
||||
{ title: 'he' }
|
||||
]
|
||||
|
||||
var test = {
|
||||
test: [1, 2, 3]
|
||||
}
|
||||
|
||||
var demo = new seed.ViewModel({
|
||||
el: '#app',
|
||||
data: {
|
||||
items: items,
|
||||
test: test
|
||||
push: function () {
|
||||
this.items.push({ title: randomChar() })
|
||||
},
|
||||
pop: function () {
|
||||
this.items.pop()
|
||||
},
|
||||
shift: function () {
|
||||
this.items.shift()
|
||||
},
|
||||
unshift: function () {
|
||||
this.items.unshift({ title: randomChar() })
|
||||
},
|
||||
splice: function () {
|
||||
this.items.splice(0, 1, { title: randomChar() }, { title: randomChar() })
|
||||
},
|
||||
replace: function () {
|
||||
this.items.replace(randomPos(), { title: randomChar() })
|
||||
},
|
||||
remove: function () {
|
||||
this.items.remove(randomPos())
|
||||
},
|
||||
sort: function () {
|
||||
this.items.sort(function (a, b) {
|
||||
return a.title.charCodeAt(0) - b.title.charCodeAt(0)
|
||||
})
|
||||
},
|
||||
reverse: function () {
|
||||
this.items.reverse()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function randomChar () {
|
||||
return String.fromCharCode(Math.floor(Math.random() * 30 + 50))
|
||||
}
|
||||
|
||||
function randomPos () {
|
||||
return Math.floor(Math.random() * items.length)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -73,7 +73,6 @@ function Compiler (vm, options) {
|
|||
var observables = this.observables = []
|
||||
var computed = this.computed = [] // computed props to parse deps from
|
||||
var ctxBindings = this.contextBindings = [] // computed props with dynamic context
|
||||
var arrays = this.arrays = []
|
||||
|
||||
// prototypal inheritance of bindings
|
||||
var parent = this.parentCompiler
|
||||
|
|
@ -114,12 +113,6 @@ function Compiler (vm, options) {
|
|||
binding = observables[i]
|
||||
Observer.observe(binding.value, binding.key, this.observer)
|
||||
}
|
||||
// emit set events for array lengths
|
||||
i = arrays.length
|
||||
while (i--) {
|
||||
binding = arrays[i]
|
||||
this.observer.emit('set', binding.key + '.length', binding.value.length)
|
||||
}
|
||||
// extract dependencies for computed properties
|
||||
if (computed.length) DepsParser.parse(computed)
|
||||
// extract dependencies for computed properties with dynamic context
|
||||
|
|
@ -148,17 +141,14 @@ CompilerProto.setupObserver = function () {
|
|||
// add own listeners which trigger binding updates
|
||||
observer
|
||||
.on('get', function (key) {
|
||||
console.log('get ' + key)
|
||||
if (bindings[key] && depsOb.isObserving) {
|
||||
depsOb.emit('get', bindings[key])
|
||||
}
|
||||
})
|
||||
.on('set', function (key, val) {
|
||||
console.log('set ' + key)
|
||||
if (bindings[key]) bindings[key].update(val)
|
||||
})
|
||||
.on('mutate', function (key) {
|
||||
console.log('mutate ' + key)
|
||||
if (bindings[key]) bindings[key].pub()
|
||||
})
|
||||
}
|
||||
|
|
@ -363,8 +353,10 @@ CompilerProto.ensurePath = function (key) {
|
|||
obj = obj[sec]
|
||||
i++
|
||||
}
|
||||
if (utils.typeOf(obj) === 'Object') {
|
||||
obj[path[i]] = obj[path[i]] || undefined
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Defines the getter/setter for a root-level binding on the VM
|
||||
|
|
@ -379,25 +371,18 @@ CompilerProto.define = function (key, binding) {
|
|||
value = binding.value = vm[key], // save the value before redefinening it
|
||||
type = utils.typeOf(value)
|
||||
|
||||
if (type === 'Object') {
|
||||
if (value.get) {// computed property
|
||||
if (type === 'Object' && value.get) {
|
||||
// computed property
|
||||
binding.isComputed = true
|
||||
binding.rawGet = value.get
|
||||
value.get = value.get.bind(vm)
|
||||
this.computed.push(binding)
|
||||
} else {
|
||||
} else if (type === 'Object' || type === 'Array') {
|
||||
// observe objects later, becase there might be more keys
|
||||
// to be added to it. we also want to emit all the set events
|
||||
// when values are available.
|
||||
// after all values are available.
|
||||
this.observables.push(binding)
|
||||
}
|
||||
} else if (type === 'Array') {
|
||||
// observe arrays right now, because they will be needed in
|
||||
// sd-each directives.
|
||||
Observer.observe(value, key, compiler.observer)
|
||||
// we need to later emit set event for the arrays length.
|
||||
this.arrays.push(binding)
|
||||
}
|
||||
|
||||
Object.defineProperty(vm, key, {
|
||||
enumerable: true,
|
||||
|
|
@ -488,8 +473,10 @@ CompilerProto.destroy = function () {
|
|||
}
|
||||
}
|
||||
// remove el
|
||||
if (el.parentNode) {
|
||||
el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers --------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
var config = require('../config'),
|
||||
utils = require('../utils'),
|
||||
Observer = require('../observer'),
|
||||
Emitter = require('emitter'),
|
||||
ViewModel // lazy def to avoid circular dependency
|
||||
|
||||
/*
|
||||
|
|
@ -10,67 +12,72 @@ var mutationHandlers = {
|
|||
|
||||
push: function (m) {
|
||||
var i, l = m.args.length,
|
||||
baseIndex = this.collection.length - l
|
||||
base = this.collection.length - l
|
||||
for (i = 0; i < l; i++) {
|
||||
this.buildItem(this.ref, m.args[i], baseIndex + i)
|
||||
this.buildItem(m.args[i], base + i)
|
||||
}
|
||||
},
|
||||
|
||||
pop: function (m) {
|
||||
m.result.$destroy()
|
||||
pop: function () {
|
||||
this.vms.pop().$destroy()
|
||||
},
|
||||
|
||||
unshift: function (m) {
|
||||
var i, l = m.args.length, ref
|
||||
var i, l = m.args.length
|
||||
for (i = 0; i < l; i++) {
|
||||
ref = this.collection.length > l
|
||||
? this.collection[l].$el
|
||||
: this.ref
|
||||
this.buildItem(ref, m.args[i], i)
|
||||
this.buildItem(m.args[i], i)
|
||||
}
|
||||
this.updateIndexes()
|
||||
},
|
||||
|
||||
shift: function (m) {
|
||||
m.result.$destroy()
|
||||
this.updateIndexes()
|
||||
shift: function () {
|
||||
this.vms.shift().$destroy()
|
||||
},
|
||||
|
||||
splice: function (m) {
|
||||
var i, pos, ref,
|
||||
l = m.args.length,
|
||||
k = m.result.length,
|
||||
var i,
|
||||
index = m.args[0],
|
||||
removed = m.args[1],
|
||||
added = l - 2
|
||||
for (i = 0; i < k; i++) {
|
||||
m.result[i].$destroy()
|
||||
added = m.args.length - 2,
|
||||
removedVMs = this.vms.splice(index, removed)
|
||||
for (i = 0; i < removed; i++) {
|
||||
removedVMs[i].$destroy()
|
||||
}
|
||||
if (added > 0) {
|
||||
for (i = 2; i < l; i++) {
|
||||
pos = index - removed + added + 1
|
||||
ref = this.collection[pos]
|
||||
? this.collection[pos].$el
|
||||
: this.ref
|
||||
this.buildItem(ref, m.args[i], index + i)
|
||||
}
|
||||
}
|
||||
if (removed !== added) {
|
||||
this.updateIndexes()
|
||||
for (i = 0; i < added; i++) {
|
||||
this.buildItem(m.args[i + 2], index + i)
|
||||
}
|
||||
},
|
||||
|
||||
sort: function () {
|
||||
var i, l = this.collection.length, viewmodel
|
||||
var key = this.arg,
|
||||
vms = this.vms,
|
||||
col = this.collection,
|
||||
l = col.length,
|
||||
sorted = new Array(l),
|
||||
i, j, vm, data
|
||||
for (i = 0; i < l; i++) {
|
||||
viewmodel = this.collection[i]
|
||||
viewmodel.$index = i
|
||||
this.container.insertBefore(viewmodel.$el, this.ref)
|
||||
data = col[i]
|
||||
for (j = 0; j < l; j++) {
|
||||
vm = vms[j]
|
||||
if (vm[key] === data) {
|
||||
sorted[i] = vm
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < l; i++) {
|
||||
this.container.insertBefore(sorted[i].$el, this.ref)
|
||||
}
|
||||
this.vms = sorted
|
||||
},
|
||||
|
||||
//mutationHandlers.reverse = mutationHandlers.sort
|
||||
reverse: function () {
|
||||
var vms = this.vms
|
||||
vms.reverse()
|
||||
for (var i = 0, l = vms.length; i < l; i++) {
|
||||
this.container.insertBefore(vms[i].$el, this.ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
|
|
@ -83,9 +90,10 @@ module.exports = {
|
|||
ctn.removeChild(this.el)
|
||||
this.collection = null
|
||||
this.vms = null
|
||||
this.mutationListener = (function (path, arr, mutation) {
|
||||
mutationHandlers[mutation.method].call(this, mutation)
|
||||
}).bind(this)
|
||||
var self = this
|
||||
this.mutationListener = function (path, arr, mutation) {
|
||||
mutationHandlers[mutation.method].call(self, mutation)
|
||||
}
|
||||
},
|
||||
|
||||
update: function (collection) {
|
||||
|
|
@ -97,28 +105,28 @@ module.exports = {
|
|||
// force a compile so that we get all the bindings for
|
||||
// dependency extraction.
|
||||
if (!this.collection && !collection.length) {
|
||||
this.buildItem(this.ref, null, true)
|
||||
this.buildItem()
|
||||
}
|
||||
this.collection = collection
|
||||
this.vms = []
|
||||
|
||||
// listen for collection mutation events
|
||||
// the collection has been augmented during Binding.set()
|
||||
if (!collection.__observer__) Observer.watchArray(collection, null, new Emitter())
|
||||
collection.__observer__.on('mutate', this.mutationListener)
|
||||
// this.compiler.observer.emit('set', this.key + '.length', collection.length)
|
||||
|
||||
// create child-seeds and append to DOM
|
||||
for (var i = 0, l = collection.length; i < l; i++) {
|
||||
var item = this.buildItem(this.ref, collection[i])
|
||||
this.container.appendChild(item.$el)
|
||||
this.vms.push(item)
|
||||
this.buildItem(collection[i], i)
|
||||
}
|
||||
},
|
||||
|
||||
buildItem: function (ref, data, dummy) {
|
||||
var node = this.el.cloneNode(true)
|
||||
this.container.insertBefore(node, ref)
|
||||
buildItem: function (data, index) {
|
||||
ViewModel = ViewModel || require('../viewmodel')
|
||||
var vmID = node.getAttribute(config.prefix + '-viewmodel'),
|
||||
var node = this.el.cloneNode(true),
|
||||
ctn = this.container,
|
||||
vmID = node.getAttribute(config.prefix + '-viewmodel'),
|
||||
ChildVM = utils.getVM(vmID) || ViewModel,
|
||||
wrappedData = {}
|
||||
wrappedData[this.arg] = data
|
||||
|
|
@ -127,20 +135,17 @@ module.exports = {
|
|||
each: true,
|
||||
eachPrefix: this.arg,
|
||||
parentCompiler: this.compiler,
|
||||
delegator: this.container,
|
||||
delegator: ctn,
|
||||
data: wrappedData
|
||||
})
|
||||
if (dummy) {
|
||||
if (!data) {
|
||||
item.$destroy()
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
},
|
||||
|
||||
updateIndexes: function () {
|
||||
var i = this.collection.length
|
||||
while (i--) {
|
||||
this.collection[i].$index = i
|
||||
var ref = this.vms.length > index
|
||||
? this.vms[index].$el
|
||||
: this.ref
|
||||
ctn.insertBefore(node, ref)
|
||||
this.vms.splice(index, 0, item)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ function watchObject (obj, path, observer) {
|
|||
}
|
||||
|
||||
function watchArray (arr, path, observer) {
|
||||
defProtected(arr, '__path__', path)
|
||||
if (path) defProtected(arr, '__path__', path)
|
||||
defProtected(arr, '__observer__', observer)
|
||||
for (var method in arrayMutators) {
|
||||
defProtected(arr, method, arrayMutators[method])
|
||||
|
|
@ -93,14 +93,21 @@ function isWatchable (obj) {
|
|||
}
|
||||
|
||||
function emitSet (obj, observer) {
|
||||
if (typeOf(obj) === 'Array') {
|
||||
observer.emit('set', 'length', obj.length)
|
||||
} else {
|
||||
var values = obj.__values__
|
||||
for (var key in values) {
|
||||
observer.emit('set', key, values[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
// used in sd-each
|
||||
watchArray: watchArray,
|
||||
|
||||
observe: function (obj, rawPath, observer) {
|
||||
if (isWatchable(obj)) {
|
||||
var path = rawPath + '.',
|
||||
|
|
@ -133,7 +140,7 @@ module.exports = {
|
|||
.on('set', proxies.set)
|
||||
.on('mutate', proxies.mutate)
|
||||
if (alreadyConverted) {
|
||||
emitSet(obj, ob)
|
||||
emitSet(obj, ob, rawPath)
|
||||
} else {
|
||||
watch(obj, null, ob)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue