Repository: sveltejs/svelte (JavaScript)
Date: 2018-04-20 19:13:39+00:00
Task type: Bug Fix

Issue:
Each blocks with spread attributes on children not updating
[REPL](https://svelte.technology/repl?version=1.60.3&gist=00df74713efec24d6b0580763e4b0edb). I *think* what's happening here is that it doesn't believe the first each block to be dynamic; it doesn't recognise that there's a dynamic attribute, so it doesn't bother writing update code. [This variant](https://svelte.technology/repl?version=1.60.3&gist=a87f1733d26f66293bcd8c9d6dab4492) works as expected, because it trips the necessary wire.
Each blocks with spread attributes on children not updating
[REPL](https://svelte.technology/repl?version=1.60.3&gist=00df74713efec24d6b0580763e4b0edb). I *think* what's happening here is that it doesn't believe the first each block to be dynamic; it doesn't recognise that there's a dynamic attribute, so it doesn't bother writing update code. [This variant](https://svelte.technology/repl?version=1.60.3&gist=a87f1733d26f66293bcd8c9d6dab4492) works as expected, because it trips the necessary wire.


Issue discussion:
Neither this trigger any wire when you change order of element in the array.
svelte v1.60.3

```html
{{#each tabs as tabID, index }}
  <Tab :tabID :index on:tabReorderStart="hTabReorderStart(event)" on:tabReorderEnd="hTabReorderEnd(event)" /> 
{{/each}}
```

tabs is a computed property, and `tabID` is a `String` not an `Object`.

```javascript
computed: {
    tabs: win => { 
        console.log(win.tabs) //this logs data change, no UI change
        return win.tabs
    }
}
```

Neither this trigger any wire when you change order of element in the array.
svelte v1.60.3

```html
{{#each tabs as tabID, index }}
  <Tab :tabID :index on:tabReorderStart="hTabReorderStart(event)" on:tabReorderEnd="hTabReorderEnd(event)" /> 
{{/each}}
```

tabs is a computed property, and `tabID` is a `String` not an `Object`.

```javascript
computed: {
    tabs: win => { 
        console.log(win.tabs) //this logs data change, no UI change
        return win.tabs
    }
}
```


Solution patch:
diff --git a/src/generators/nodes/Component.ts b/src/generators/nodes/Component.ts
--- a/src/generators/nodes/Component.ts
+++ b/src/generators/nodes/Component.ts
@@ -37,7 +37,7 @@ export default class Component extends Node {
 					attribute.expression.arguments.forEach((arg: Node) => {
 						block.addDependencies(arg.metadata.dependencies);
 					});
-				} else if (attribute.type === 'Binding') {
+				} else if (attribute.type === 'Binding' || attribute.type === 'Spread') {
 					block.addDependencies(attribute.metadata.dependencies);
 				}
 			}
diff --git a/src/generators/nodes/Element.ts b/src/generators/nodes/Element.ts
--- a/src/generators/nodes/Element.ts
+++ b/src/generators/nodes/Element.ts
@@ -89,6 +89,8 @@ export default class Element extends Node {
 					}
 				} else if (attribute.type === 'Action' && attribute.expression) {
 					block.addDependencies(attribute.metadata.dependencies);
+				} else if (attribute.type === 'Spread') {
+					block.addDependencies(attribute.metadata.dependencies);
 				}
 			}
 		});
diff --git a/src/utils/flattenReference.ts b/src/utils/flattenReference.ts
--- a/src/utils/flattenReference.ts
+++ b/src/utils/flattenReference.ts
@@ -1,6 +1,6 @@
 import { Node } from '../interfaces';
 
-export default function flatten(node: Node) {
+export default function flattenReference(node: Node) {
 	const parts = [];
 	const propEnd = node.end;
 


Test patch:
diff --git a/test/runtime/samples/spread-each-component/Nested.html b/test/runtime/samples/spread-each-component/Nested.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-each-component/Nested.html
@@ -0,0 +1 @@
+<div data-a={a} data-b={b}></div>
\ No newline at end of file
diff --git a/test/runtime/samples/spread-each-component/_config.js b/test/runtime/samples/spread-each-component/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-each-component/_config.js
@@ -0,0 +1,26 @@
+export default {
+	html: `
+		<div data-a="1" data-b="2"></div>
+		<div data-a="3" data-b="4"></div>
+	`,
+
+	data: {
+		things: [
+			{ a: 1, b: 2 },
+			{ a: 3, b: 4 }
+		]
+	},
+
+	test(assert, component, target) {
+		const { things } = component.get();
+
+		component.set({
+			things: things.reverse()
+		});
+
+		assert.htmlEqual(target.innerHTML, `
+			<div data-a="3" data-b="4"></div>
+			<div data-a="1" data-b="2"></div>
+		`);
+	},
+};
diff --git a/test/runtime/samples/spread-each-component/main.html b/test/runtime/samples/spread-each-component/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-each-component/main.html
@@ -0,0 +1,11 @@
+{#each things as thing}
+	<Nested {...thing}/>
+{/each}
+
+<script>
+	import Nested from './Nested.html';
+
+	export default {
+		components: { Nested }
+	};
+</script>
\ No newline at end of file
diff --git a/test/runtime/samples/spread-each-element/_config.js b/test/runtime/samples/spread-each-element/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-each-element/_config.js
@@ -0,0 +1,26 @@
+export default {
+	html: `
+		<div data-a="1" data-b="2"></div>
+		<div data-c="3" data-d="4"></div>
+	`,
+
+	data: {
+		things: [
+			{ 'data-a': 1, 'data-b': 2 },
+			{ 'data-c': 3, 'data-d': 4 }
+		]
+	},
+
+	test(assert, component, target) {
+		const { things } = component.get();
+
+		component.set({
+			things: things.reverse()
+		});
+
+		assert.htmlEqual(target.innerHTML, `
+			<div data-c="3" data-d="4"></div>
+			<div data-a="1" data-b="2"></div>
+		`);
+	},
+};
diff --git a/test/runtime/samples/spread-each-element/main.html b/test/runtime/samples/spread-each-element/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-each-element/main.html
@@ -0,0 +1,3 @@
+{#each things as thing}
+	<div {...thing}></div>
+{/each}
\ No newline at end of file