Evan You %!s(int64=11) %!d(string=hai) anos
pai
achega
e91e5ce8a5
Modificáronse 5 ficheiros con 71 adicións e 18 borrados
  1. 25 11
      changes.md
  2. 7 5
      src/api/data.js
  3. 7 1
      src/watcher.js
  4. 17 1
      test/unit/specs/api/data_spec.js
  5. 15 0
      test/unit/specs/watcher_spec.js

+ 25 - 11
changes.md

@@ -176,20 +176,34 @@ By default, all child components **DO NOT** inherit the parent scope. Only anony
   })
   ```
 
-  By default the callback only fires when the value changes. If you want it to be called immediately with the initial value, use the third optional `immediate` argument:
+  - **Deep watching**
 
-  ``` js
-  vm.$watch('a', callback, true)
-  // callback is fired immediately with current value of `a`
-  ```
+    (Breaking) A change from 0.11 is that `$watch` now by default only fires when the identity of the watched value changes. If you want the watcher to also fire the callback when a nested value changes, pass in the third optional `deep` argument:
+
+    ``` js
+    vm.$watch('someObject', callback, true)
+    vm.someObject.nestedValue = 123
+    // callback is fired
+    ```
 
-- (Breaking) `$unwatch` has been removed. `$watch` now also returns an unregister function:
+  - **Immediate invocation**
 
-  ``` js
-  var unwatch = vm.$watch('a', cb)
-  // later, teardown the watcher
-  unwatch()
-  ```
+    By default the callback only fires when the value changes. If you want it to be called immediately with the initial value, use the fourth optional `immediate` argument:
+
+    ``` js
+    vm.$watch('a', callback, false, true)
+    // callback is fired immediately with current value of `a`
+    ```
+
+  - **Unwatching**
+
+    (Breaking) `$unwatch` has been removed. `$watch` now also returns an unregister function:
+
+    ``` js
+    var unwatch = vm.$watch('a', cb)
+    // later, teardown the watcher
+    unwatch()
+    ```
 
 - `vm.$get` now accepts expressions:
 

+ 7 - 5
src/api/data.js

@@ -62,19 +62,21 @@ exports.$delete = function (key) {
  *
  * @param {String} exp
  * @param {Function} cb
+ * @param {Boolean} [deep]
  * @param {Boolean} [immediate]
  * @return {Function} - unwatchFn
  */
 
-exports.$watch = function (exp, cb, immediate) {
+exports.$watch = function (exp, cb, deep, immediate) {
   var vm = this
-  var watcher = vm._userWatchers[exp]
+  var key = deep ? exp + '**deep**' : exp
+  var watcher = vm._userWatchers[key]
   var wrappedCb = function (val, oldVal) {
     cb.call(vm, val, oldVal)
   }
   if (!watcher) {
-    watcher = vm._userWatchers[exp] =
-      new Watcher(vm, exp, wrappedCb)
+    watcher = vm._userWatchers[key] =
+      new Watcher(vm, exp, wrappedCb, null, false, deep)
   } else {
     watcher.addCb(wrappedCb)
   }
@@ -84,7 +86,7 @@ exports.$watch = function (exp, cb, immediate) {
   return function unwatchFn () {
     watcher.removeCb(wrappedCb)
     if (!watcher.active) {
-      vm._userWatchers[exp] = null
+      vm._userWatchers[key] = null
     }
   }
 }

+ 7 - 1
src/watcher.js

@@ -16,16 +16,18 @@ var uid = 0
  * @param {Function} cb
  * @param {Array} [filters]
  * @param {Boolean} [needSet]
+ * @param {Boolean} [deep]
  * @constructor
  */
 
-function Watcher (vm, expression, cb, filters, needSet) {
+function Watcher (vm, expression, cb, filters, needSet, deep) {
   this.vm = vm
   vm._watcherList.push(this)
   this.expression = expression
   this.cbs = [cb]
   this.id = ++uid // uid for batching
   this.active = true
+  this.deep = deep
   this.deps = Object.create(null)
   // setup filters if any.
   // We delegate directive filters here to the watcher
@@ -67,6 +69,10 @@ p.get = function () {
   this.beforeGet()
   var vm = this.vm
   var value = this.getter.call(vm, vm)
+  // use JSON.stringify to "touch" every property
+  // so they are all tracked as dependencies for
+  // deep watching
+  if (this.deep) JSON.stringify(value)
   value = _.applyFilters(value, this.readFilters, vm)
   this.afterGet()
   return value

+ 17 - 1
test/unit/specs/api/data_spec.js

@@ -85,7 +85,8 @@ describe('Data API', function () {
 
   it('$watch', function (done) {
     var spy = jasmine.createSpy()
-    var unwatch = vm.$watch('a + b.c', spy, true)
+    // test immediate invoke
+    var unwatch = vm.$watch('a + b.c', spy, false, true)
     expect(spy).toHaveBeenCalledWith(3, undefined)
     vm.a = 2
     nextTick(function () {
@@ -111,6 +112,21 @@ describe('Data API', function () {
     })
   })
 
+  it('deep $watch', function (done) {
+    var oldB = vm.b
+    var spy = jasmine.createSpy()
+    vm.$watch('b', spy, true)
+    vm.b.c = 3
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(oldB, oldB)
+      vm.b = { c: 4 }
+      nextTick(function () {
+        expect(spy).toHaveBeenCalledWith(vm.b, oldB)
+        done()
+      })
+    })
+  })
+
   it('$eval', function () {
     expect(vm.$eval('a')).toBe(1)
     expect(vm.$eval('b.c')).toBe(2)

+ 15 - 0
test/unit/specs/watcher_spec.js

@@ -285,6 +285,21 @@ describe('Watcher', function () {
     })
   })
 
+  it('deep watch', function (done) {
+    var watcher = new Watcher(vm, 'b', spy, null, false, true)
+    vm.b.c = 3
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
+      var oldB = vm.b
+      vm.b = { c: 4 }
+      nextTick(function () {
+        expect(spy).toHaveBeenCalledWith(vm.b, oldB)
+        expect(spy.calls.count()).toBe(2)
+        done()
+      })
+    })
+  })
+
   it('add callback', function (done) {
     var watcher = new Watcher(vm, 'a', spy)
     var spy2 = jasmine.createSpy()