Ver Fonte

Feature - deep $watch (#2462)

* Add failing test

* Enable deep watching

* Update docs
Simone Todaro há 3 anos atrás
pai
commit
eafa3db40f

+ 2 - 3
packages/alpinejs/src/magics/$watch.js

@@ -10,9 +10,8 @@ magic('watch', el => (key, callback) => {
     let oldValue
 
     effect(() => evaluate(value => {
-        // This is a hack to force deep reactivity for things like "items.push()".
-        let div = document.createElement('div')
-        div.dataset.throwAway = value
+        // JSON.stringify touches every single property at any level enabling deep watching
+        JSON.stringify(value)
 
         if (! firstTime) {
             // We have to queue this watcher as a microtask so that

+ 22 - 0
packages/docs/src/en/magics/watch.md

@@ -35,3 +35,25 @@ When the `<button>` is pressed, `foo.bar` will be set to "bob", and "bob" will b
     <button @click="open = ! open">Toggle Open</button>
 </div>
 ```
+
+<a name="deep-watching"></a>
+### Deep watching
+
+`$watch` will automatically watches from changes at any level but you should keep in mind that, when a change is detected, the watcher will return the value of the observed property, not the value of the subproperty that has changed.
+
+```alpine
+<div x-data="{ foo: { bar: 'baz' }}" x-init="$watch('foo', (value, oldValue) => console.log(value, oldValue))">
+    <button @click="foo.bar = 'bob'">Update</button>
+</div>
+```
+
+When the `<button>` is pressed, `foo.bar` will be set to "bob", and "{bar: 'bob'} {bar: 'baz'}" will be logged to the console (new and old value).
+
+> ⚠️ Changing a property of a "watched" object as a side effect of the `$watch` callback will generate an infinite loop and eventually error. 
+
+```alpine
+<!-- 🚫 Infinite loop -->
+<div x-data="{ foo: { bar: 'baz', bob: 'lob' }}" x-init="$watch('foo', value => foo.bob = foo.bar">
+    <button @click="foo.bar = 'bob'">Update</button>
+</div>
+```

+ 22 - 0
tests/cypress/integration/magics/$watch.spec.js

@@ -148,3 +148,25 @@ test('$watch ignores other dependencies',
         get('span').should(haveText('1'))
     }
 )
+
+
+test('deep $watch',
+    html`
+        <div x-data="{ foo: { bar: 'baz'}, bob: 'lob' }" x-init="
+            $watch('foo', value => { bob = value.bar }, {deep: true});
+        ">
+            <h1 x-text="foo.bar"></h1>
+            <h2 x-text="bob"></h2>
+
+            <button x-on:click="foo.bar = 'law'"></button>
+        </div>
+    `,
+    ({ get }) => {
+        get('h1').should(haveText('baz'))
+        get('h2').should(haveText('lob'))
+        get('button').click()
+        get('h1').should(haveText('law'))
+        get('h2').should(haveText('law'))
+    }
+)
+