-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #432 from sveltejs/binding-mutation-bug
prevent hard-to-reproduce bug with deep two-way bindings
- Loading branch information
Showing
9 changed files
with
144 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,19 @@ | ||
export default function flatten ( node ) { | ||
const parts = []; | ||
const propEnd = node.end; | ||
|
||
while ( node.type === 'MemberExpression' ) { | ||
if ( node.computed ) return null; | ||
parts.unshift( node.property.name ); | ||
|
||
node = node.object; | ||
} | ||
|
||
const propStart = node.end; | ||
const name = node.type === 'Identifier' ? node.name : node.type === 'ThisExpression' ? 'this' : null; | ||
|
||
if ( !name ) return null; | ||
|
||
parts.unshift( name ); | ||
return { name, parts, keypath: parts.join( '.' ) }; | ||
return { name, parts, keypath: `${name}[✂${propStart}-${propEnd}✂]` }; | ||
} |
5 changes: 5 additions & 0 deletions
5
test/generator/samples/component-binding-deep-b/ComponentSelector.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<select bind:value='selectedComponent'> | ||
{{#each components as component}} | ||
<option value='{{component}}'>{{component.name}}.html</option> | ||
{{/each}} | ||
</select> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<textarea bind:value='code'></textarea> |
84 changes: 84 additions & 0 deletions
84
test/generator/samples/component-binding-deep-b/_config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
const components = [ | ||
{ | ||
name: 'One', | ||
source: 'one source' | ||
}, | ||
{ | ||
name: 'Two', | ||
source: 'two source' | ||
} | ||
]; | ||
|
||
const selectedComponent = components[0]; | ||
|
||
export default { | ||
skip: true, // doesn't reflect real-world bug, maybe a JSDOM quirk | ||
|
||
data: { | ||
components, | ||
selectedComponent | ||
}, | ||
|
||
html: ` | ||
<select> | ||
<option value='[object Object]'>One.html</option> | ||
<option value='[object Object]'>Two.html</option> | ||
</select> | ||
<textarea></textarea> | ||
<pre>ONE SOURCE\nTWO SOURCE</pre> | ||
`, | ||
|
||
test ( assert, component, target, window ) { | ||
const event = new window.MouseEvent( 'input' ); | ||
const textarea = target.querySelector( 'textarea' ); | ||
|
||
textarea.value = 'one source changed'; | ||
textarea.dispatchEvent( event ); | ||
|
||
assert.equal( component.get( 'compiled' ), 'ONE SOURCE CHANGED\nTWO SOURCE' ); | ||
assert.htmlEqual( target.innerHTML, ` | ||
<select> | ||
<option value='[object Object]'>One.html</option> | ||
<option value='[object Object]'>Two.html</option> | ||
</select> | ||
<textarea></textarea> | ||
<pre>ONE SOURCE CHANGED\nTWO SOURCE</pre> | ||
` ); | ||
|
||
// const select = target.querySelector( 'select' ); | ||
// console.log( `select.options[0].selected`, select.options[0].selected ) | ||
// console.log( `select.options[1].selected`, select.options[1].selected ) | ||
// console.log( `select.value`, select.value ) | ||
// console.log( `select.__value`, select.__value ) | ||
// select.options[1].selected = true; | ||
// console.log( `select.options[0].selected`, select.options[0].selected ) | ||
// console.log( `select.options[1].selected`, select.options[1].selected ) | ||
// console.log( `select.value`, select.value ) | ||
// console.log( `select.__value`, select.__value ) | ||
// select.dispatchEvent( new window.Event( 'change' ) ); | ||
component.set({ selectedComponent: components[1] }); | ||
|
||
assert.equal( textarea.value, 'two source' ); | ||
|
||
textarea.value = 'two source changed'; | ||
textarea.dispatchEvent( event ); | ||
|
||
assert.equal( component.get( 'compiled' ), 'ONE SOURCE CHANGED\nTWO SOURCE CHANGED' ); | ||
assert.htmlEqual( target.innerHTML, ` | ||
<select> | ||
<option value='[object Object]'>One.html</option> | ||
<option value='[object Object]'>Two.html</option> | ||
</select> | ||
<textarea></textarea> | ||
<pre>ONE SOURCE CHANGED\nTWO SOURCE CHANGED</pre> | ||
` ); | ||
|
||
component.destroy(); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<ComponentSelector :components bind:selectedComponent/> | ||
<Editor bind:code='selectedComponent.source'/> | ||
|
||
<pre> | ||
{{compiled}} | ||
</pre> | ||
|
||
<script> | ||
import Editor from './Editor.html'; | ||
import ComponentSelector from './ComponentSelector.html'; | ||
|
||
export default { | ||
components: { | ||
ComponentSelector, | ||
Editor | ||
}, | ||
|
||
oncreate () { | ||
this.observe( 'components', components => { | ||
components.forEach( component => { | ||
if ( component === this.get( 'selectedComponent' ) ) return; | ||
component.compiled = component.source.toUpperCase(); | ||
}); | ||
}); | ||
|
||
this.observe( 'selectedComponent', component => { | ||
component.compiled = component.source.toUpperCase(); | ||
this.updateBundle(); | ||
}); | ||
}, | ||
|
||
methods: { | ||
updateBundle () { | ||
const components = this.get( 'components' ); | ||
|
||
const compiled = components.map( component => component.compiled ).join( '\n' ); | ||
|
||
this.set({ compiled }); | ||
} | ||
} | ||
} | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters