Parcourir la source

Object.$add/$delete + renamed Array augmentation methods to prefix with $

Evan You il y a 12 ans
Parent
commit
25344cf467

+ 2 - 2
examples/todomvc/js/app.js

@@ -94,7 +94,7 @@
             },
 
             removeTodo: function (todo) {
-                this.todos.remove(todo.$data);
+                this.todos.$remove(todo.$data);
                 todoStorage.save();
             },
 
@@ -125,7 +125,7 @@
             },
             
             removeCompleted: function () {
-                this.todos.remove(function (todo) {
+                this.todos.$remove(function (todo) {
                     return todo.completed;
                 });
                 todoStorage.save();

+ 50 - 48
src/directives/repeat.js

@@ -1,7 +1,6 @@
 var Observer   = require('../observer'),
     utils      = require('../utils'),
-    config     = require('../config'),
-    def        = utils.defProtected
+    config     = require('../config')
 
 /**
  *  Mathods that perform precise DOM manipulation
@@ -151,6 +150,12 @@ module.exports = {
             if (!init) this.changed()
         }
 
+        // listen for object changes and sync the repeater
+        if (this.object) {
+            this.object.__emitter__.on('set', this.syncRepeater)
+            this.object.__emitter__.on('delete', this.deleteProp)
+        }
+
         // destroy unused old VMs
         if (oldVMs) destroyVMs(oldVMs)
         this.old = this.oldVMs = null
@@ -159,8 +164,7 @@ module.exports = {
     addItems: function (data, base) {
         base = base || 0
         for (var i = 0, l = data.length; i < l; i++) {
-            var vm = this.build(data[i], base + i)
-            this.updateObject(vm, 1)
+            this.build(data[i], base + i)
         }
     },
 
@@ -168,7 +172,6 @@ module.exports = {
         var i = data.length
         while (i--) {
             data[i].$destroy()
-            this.updateObject(data[i], -1)
         }
     },
 
@@ -281,7 +284,7 @@ module.exports = {
             if (nonObject || self.arg) {
                 var sync = function (val) {
                     self.lock = true
-                    self.collection.set(item.$index, val)
+                    self.collection.$set(item.$index, val)
                     self.lock = false
                 }
                 item.$compiler.observer.on('change:' + (self.arg || '$value'), sync)
@@ -315,8 +318,6 @@ module.exports = {
                 }
             }
         }
-
-        return item
     },
 
     /**
@@ -325,56 +326,54 @@ module.exports = {
      */
     convertObject: function (object) {
 
-        if (this.object) {
-            this.object.__emitter__.off('set', this.updateRepeater)
-        }
-
         this.object = object
-        var collection = object.$repeater || objectToArray(object)
-        if (!object.$repeater) {
-            def(object, '$repeater', collection)
-        }
-
-        var self = this
-        this.updateRepeater = function (key, val) {
-            if (key.indexOf('.') === -1) {
-                var i = self.vms.length, item
-                while (i--) {
-                    item = self.vms[i]
-                    if (item.$key === key) {
-                        if (item.$data !== val && item.$value !== val) {
-                            if ('$value' in item) {
-                                item.$value = val
-                            } else {
-                                item.$data = val
-                            }
+        var self = this,
+            collection = objectToArray(object)
+
+        this.syncRepeater = function (key, val) {
+            if (key in object) {
+                var vm = self.findVMByKey(key)
+                if (vm) {
+                    // existing vm, update property
+                    if (vm.$data !== val && vm.$value !== val) {
+                        if ('$value' in vm) {
+                            vm.$value = val
+                        } else {
+                            vm.$data = val
+                        }
+                    }
+                } else {
+                    // new property added!
+                    var data
+                    if (utils.typeOf(val) === 'Object') {
+                        data = val
+                        data.$key = key
+                    } else {
+                        data = {
+                            $key: key,
+                            $value: val
                         }
-                        break
                     }
+                    collection.push(data)
                 }
             }
         }
 
-        object.__emitter__.on('set', this.updateRepeater)
+        this.deleteProp = function (key) {
+            var i = self.findVMByKey(key).$index
+            collection.splice(i, 1)
+        }
+
         return collection
     },
 
-    /**
-     *  Sync changes from the $repeater Array
-     *  back to the represented Object
-     */
-    updateObject: function (vm, action) {
-        var obj = this.object
-        if (obj && vm.$key) {
-            var key = vm.$key,
-                val = vm.$value || vm.$data
-            if (action > 0) { // new property
-                obj[key] = val
-                Observer.convertKey(obj, key)
-            } else {
-                delete obj[key]
+    findVMByKey: function (key) {
+        var i = this.vms.length, vm
+        while (i--) {
+            vm = this.vms[i]
+            if (vm.$key === key) {
+                return vm
             }
-            obj.__emitter__.emit('set', key, val, true)
         }
     },
 
@@ -382,6 +381,9 @@ module.exports = {
         if (this.childId) {
             delete this.vm.$[this.childId]
         }
+        if (this.object) {
+            this.object.__emitter__.off('set', this.updateRepeater)
+        }
         if (this.collection) {
             this.collection.__emitter__.off('mutate', this.mutationListener)
             if (destroy) {
@@ -407,7 +409,7 @@ function objectToArray (obj) {
         data = utils.typeOf(val) === 'Object'
             ? val
             : { $value: val }
-        def(data, '$key', key)
+        data.$key = key
         res.push(data)
     }
     return res

+ 39 - 10
src/observer.js

@@ -35,9 +35,9 @@ var ArrayProxy = Object.create(Array.prototype)
 ].forEach(watchMutation)
 
 // Augment the ArrayProxy with convenience methods
-def(ArrayProxy, 'remove', removeElement, !hasProto)
-def(ArrayProxy, 'set', replaceElement, !hasProto)
-def(ArrayProxy, 'replace', replaceElement, !hasProto)
+def(ArrayProxy, '$remove', removeElement, !hasProto)
+def(ArrayProxy, '$set', replaceElement, !hasProto)
+def(ArrayProxy, '$replace', replaceElement, !hasProto)
 
 /**
  *  Intercep a mutation event so we can emit the mutation info.
@@ -166,6 +166,26 @@ function replaceElement (index, data) {
     }
 }
 
+// Object add/delete key augmentation -----------------------------------------
+
+var ObjProxy = Object.create(Object.prototype)
+
+def(ObjProxy, '$add', function (key, val) {
+    if (key in this) return
+    this[key] = val
+    convertKey(this, key)
+    // emit a propagating set event
+    this.__emitter__.emit('set', key, val, true)
+}, !hasProto)
+
+def(ObjProxy, '$delete', function (key) {
+    if (!(key in this)) return
+    // trigger set events
+    this[key] = undefined
+    delete this[key]
+    this.__emitter__.emit('delete', key)
+}, !hasProto)
+
 // Watch Helpers --------------------------------------------------------------
 
 /**
@@ -208,10 +228,25 @@ function watch (obj) {
     }
 }
 
+/**
+ *  Augment target objects with modified
+ *  methods
+ */
+function augment (target, src) {
+    if (hasProto) {
+        target.__proto__ = src
+    } else {
+        for (var key in src) {
+            def(target, key, src[key])
+        }
+    }
+}
+
 /**
  *  Watch an Object, recursive.
  */
 function watchObject (obj) {
+    augment(obj, ObjProxy)
     for (var key in obj) {
         convertKey(obj, key)
     }
@@ -222,13 +257,7 @@ function watchObject (obj) {
  *  and add augmentations by intercepting the prototype chain
  */
 function watchArray (arr) {
-    if (hasProto) {
-        arr.__proto__ = ArrayProxy
-    } else {
-        for (var key in ArrayProxy) {
-            def(arr, key, ArrayProxy[key])
-        }
-    }
+    augment(arr, ArrayProxy)
     linkArrayElements(arr, arr)
 }
 

+ 7 - 20
test/functional/fixtures/repeat-object.html

@@ -7,10 +7,9 @@
     </ul>
     <button id="push" v-on="click:t1">push to primitive</button>
     <button id="pop" v-on="click:t2">pop from primitive</button>
-    <button id="shift" v-on="click:t3">shift from object</button>
-    <button id="unshift" v-on="click:t4">unshift to object</button>
-    <button id="splice" v-on="click:t5">splice in object</button>
-    <button id="set" v-on="click:t6">Setting the original object</button>
+    <button id="delete" v-on="click:t3">delete prop from object</button>
+    <button id="add" v-on="click:t4">add prop to object</button>
+    <button id="set" v-on="click:t5">Setting the original object</button>
     <p id="primitive">{{primitive}}</p>
     <p id="obj">{{obj}}</p>
 </div>
@@ -31,30 +30,18 @@ var test = new Vue({
     },
     methods: {
         t1: function () {
-            this.primitive.$repeater.push({
-                $key: 'c',
-                $value: 3
-            })  
+            this.primitive.$add('c', 3)
         },
         t2: function () {
-            this.primitive.$repeater.pop()
+            this.primitive.$delete('c')
         },
         t3: function () {
-            this.obj.$repeater.shift()
+            this.obj.$delete('a')
         },
         t4: function () {
-            this.obj.$repeater.unshift({
-                $key: 'c',
-                msg: 'ho!'
-            })
+            this.obj.$add('c', { msg: 'ho!' })
         },
         t5: function () {
-            this.obj.$repeater.splice(1, 1, {
-                $key: 'd',
-                msg: 'he!'
-            })
-        },
-        t6: function () {
             this.primitive.a = 3
             this.obj.c = { msg: 'hu!' }
         }

+ 2 - 2
test/functional/fixtures/repeated-items.html

@@ -49,10 +49,10 @@
                 this.items.splice(1, 1, { title: getChar() }, { title: getChar() })
             },
             replace: function () {
-                this.items.replace(getPos(), { title: getChar() })
+                this.items.$replace(getPos(), { title: getChar() })
             },
             remove: function () {
-                this.items.remove(getPos())
+                this.items.$remove(getPos())
             },
             sort: function () {
                 this.items.sort(function (a, b) {

+ 6 - 3
test/functional/fixtures/routing.html

@@ -43,8 +43,8 @@
             <a href="#!/{{$value}}" v-class="current:currentView == $value">{{$value}}</a>
         </li>
     </ul>
-    <div v-view="currentView" class="view" v-transition>
-        <p>Hello! {{msg}}</p>
+    <div v-view="currentView" class="view" v-with="global:data" v-transition>
+        <p>Hello! {{msg}} {{global.test}}</p>
     </div>
 </div>
 
@@ -94,7 +94,10 @@ var app = new Vue({
     el: 'div',
     data: {
         currentView: getRoute(),
-        routes: routes
+        routes: routes,
+        data: {
+            test: '123'
+        }
     }
 })
 

+ 7 - 13
test/functional/specs/repeat-object.js

@@ -1,4 +1,4 @@
-casper.test.begin('Repeat properties of an Object', 28, function (test) {
+casper.test.begin('Repeat properties of an Object', 24, function (test) {
     
     casper
     .start('./fixtures/repeat-object.html')
@@ -21,29 +21,23 @@ casper.test.begin('Repeat properties of an Object', 28, function (test) {
         test.assertElementCount('.primitive', 2)
         test.assertSelectorHasText('#primitive', '{"a":1,"b":2}')
     })
-    .thenClick('#shift', function () {
+    .thenClick('#delete', function () {
         test.assertElementCount('.obj', 1)
         test.assertSelectorHasText('.obj:nth-child(1)', 'b ha!')
         test.assertSelectorHasText('#obj', '{"b":{"msg":"ha!"}}')
     })
-    .thenClick('#unshift', function () {
+    .thenClick('#add', function () {
         test.assertElementCount('.obj', 2)
-        test.assertSelectorHasText('.obj:nth-child(1)', 'c ho!')
-        test.assertSelectorHasText('.obj:nth-child(2)', 'b ha!')
+        test.assertSelectorHasText('.obj:nth-child(1)', 'b ha!')
+        test.assertSelectorHasText('.obj:nth-child(2)', 'c ho!')
         test.assertSelectorHasText('#obj', '{"b":{"msg":"ha!"},"c":{"msg":"ho!"}}')
     })
-    .thenClick('#splice', function () {
-        test.assertElementCount('.obj', 2)
-        test.assertSelectorHasText('.obj:nth-child(1)', 'c ho!')
-        test.assertSelectorHasText('.obj:nth-child(2)', 'd he!')
-        test.assertSelectorHasText('#obj', '{"c":{"msg":"ho!"},"d":{"msg":"he!"}}')
-    })
     // changing the object syncs to repeater
     .thenClick('#set', function () {
         test.assertSelectorHasText('.primitive:nth-child(1)', 'a 3')
-        test.assertSelectorHasText('.obj:nth-child(1)', 'c hu!')
+        test.assertSelectorHasText('.obj:nth-child(2)', 'c hu!')
         test.assertSelectorHasText('#primitive', '{"a":3,"b":2}')
-        test.assertSelectorHasText('#obj', '{"c":{"msg":"hu!"},"d":{"msg":"he!"}}')
+        test.assertSelectorHasText('#obj', '{"b":{"msg":"ha!"},"c":{"msg":"hu!"}}')
     })
     .run(function () {
         test.done()

+ 2 - 2
test/unit/specs/directives.js

@@ -862,7 +862,7 @@ describe('Directives', function () {
             })
 
             function testAddKey () {
-                v.obj.$repeater.push({ $key: 'c', msg: 'he!' })
+                v.obj.$add('c', { msg: 'he!' })
                 nextTick(function () {
                     assert.strictEqual(v.$el.textContent, 'a ho!b ha!c he!')
                     assert.strictEqual(v.obj.c.msg, 'he!')
@@ -871,7 +871,7 @@ describe('Directives', function () {
             }
 
             function testRemoveKey () {
-                v.obj.$repeater.shift()
+                v.obj.$delete('a')
                 nextTick(function () {
                     assert.strictEqual(v.$el.textContent, 'b ha!c he!')
                     assert.strictEqual(v.obj.a, undefined)

+ 12 - 12
test/unit/specs/observer.js

@@ -270,7 +270,7 @@ describe('Observer', function () {
 
         describe('Augmentations', function () {
 
-            it('remove (index)', function () {
+            it('$remove (index)', function () {
                 var emitted = false,
                     index = ~~(Math.random() * arr.length),
                     expected = arr[index] = { a: 1 }
@@ -280,12 +280,12 @@ describe('Observer', function () {
                     assert.strictEqual(mutation.args.length, 2)
                     assert.strictEqual(mutation.args[0], index)
                 })
-                var r = arr.remove(index)
+                var r = arr.$remove(index)
                 assert.ok(emitted)
                 assert.strictEqual(r, expected)
             })
             
-            it('remove (object)', function () {
+            it('$remove (object)', function () {
                 var emitted = false,
                     index = ~~(Math.random() * arr.length),
                     expected = arr[index] = { a: 1 }
@@ -295,12 +295,12 @@ describe('Observer', function () {
                     assert.strictEqual(mutation.args.length, 2)
                     assert.strictEqual(mutation.args[0], index)
                 })
-                var r = arr.remove(expected)
+                var r = arr.$remove(expected)
                 assert.ok(emitted)
                 assert.strictEqual(r, expected)
             })
 
-            it('remove (function)', function () {
+            it('$remove (function)', function () {
                 var expected = [1001, 1002]
                 arr.push.apply(arr, expected)
                 var filter = function (e) {
@@ -309,12 +309,12 @@ describe('Observer', function () {
                     copy = arr.filter(function (e) {
                         return e <= 1000
                     })
-                var removed = arr.remove(filter)
+                var removed = arr.$remove(filter)
                 assert.deepEqual(arr, copy)
                 assert.deepEqual(expected, removed)
             })
 
-            it('replace (index)', function () {
+            it('$replace (index)', function () {
                 var emitted = false,
                     index = ~~(Math.random() * arr.length),
                     expected = arr[index] = { a: 1 },
@@ -325,13 +325,13 @@ describe('Observer', function () {
                     assert.strictEqual(mutation.args.length, 3)
                     assert.strictEqual(mutation.args[0], index)
                 })
-                var r = arr.replace(index, arg)
+                var r = arr.$replace(index, arg)
                 assert.ok(emitted)
                 assert.strictEqual(r, expected)
                 assert.strictEqual(arr[index], arg)
             })
 
-            it('replace (object)', function () {
+            it('$replace (object)', function () {
                 var emitted = false,
                     index = ~~(Math.random() * arr.length),
                     expected = arr[index] = { a: 1 },
@@ -342,19 +342,19 @@ describe('Observer', function () {
                     assert.strictEqual(mutation.args.length, 3)
                     assert.strictEqual(mutation.args[0], index)
                 })
-                var r = arr.replace(expected, arg)
+                var r = arr.$replace(expected, arg)
                 assert.ok(emitted)
                 assert.strictEqual(r, expected)
                 assert.strictEqual(arr[index], arg)
             })
 
-            it('replace (function)', function () {
+            it('$replace (function)', function () {
                 arr[0] = 1
                 arr[1] = 2
                 arr[2] = 3
                 var expected = [2, 3, 3],
                     expectRet = [1, 2]
-                var replaced = arr.replace(function (e) {
+                var replaced = arr.$replace(function (e) {
                     if (e < 3) return e + 1
                 })
                 assert.deepEqual(arr, expected)