Merge branch 'master' into gh-1287

pull/1384/head
Rich Harris 7 years ago
commit dc0cb60bfc

@ -16,7 +16,7 @@ export default class EachBlock extends Node {
iterations: string; iterations: string;
index: string; index: string;
context: string; context: string;
key: string; key: Expression;
scope: TemplateScope; scope: TemplateScope;
destructuredContexts: string[]; destructuredContexts: string[];
@ -29,7 +29,10 @@ export default class EachBlock extends Node {
this.expression = new Expression(compiler, this, scope, info.expression); this.expression = new Expression(compiler, this, scope, info.expression);
this.context = info.context; this.context = info.context;
this.index = info.index; this.index = info.index;
this.key = info.key;
this.key = info.key
? new Expression(compiler, this, scope, info.key)
: null;
this.scope = scope.child(); this.scope = scope.child();
@ -270,7 +273,7 @@ export default class EachBlock extends Node {
mountOrIntro, mountOrIntro,
} }
) { ) {
const key = block.getUniqueName('key'); const get_key = block.getUniqueName('get_key');
const blocks = block.getUniqueName(`${each}_blocks`); const blocks = block.getUniqueName(`${each}_blocks`);
const lookup = block.getUniqueName(`${each}_lookup`); const lookup = block.getUniqueName(`${each}_lookup`);
@ -290,11 +293,14 @@ export default class EachBlock extends Node {
} }
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
const ${get_key} = ctx => ${this.key.snippet};
for (var #i = 0; #i < ${each_block_value}.${length}; #i += 1) { for (var #i = 0; #i < ${each_block_value}.${length}; #i += 1) {
var ${key} = ${each_block_value}[#i].${this.key}; let child_ctx = @assign(@assign({}, ctx), {
${blocks}[#i] = ${lookup}[${key}] = ${create_each_block}(#component, ${key}, @assign(@assign({}, ctx), {
${this.contextProps.join(',\n')} ${this.contextProps.join(',\n')}
})); });
let key = ${get_key}(child_ctx);
${blocks}[#i] = ${lookup}[key] = ${create_each_block}(#component, key, child_ctx);
} }
`); `);
@ -321,7 +327,7 @@ export default class EachBlock extends Node {
block.builders.update.addBlock(deindent` block.builders.update.addBlock(deindent`
var ${each_block_value} = ${snippet}; var ${each_block_value} = ${snippet};
${blocks} = @updateKeyedEach(${blocks}, #component, changed, "${this.key}", ${dynamic ? '1' : '0'}, ${each_block_value}, ${lookup}, ${updateMountNode}, ${String(this.block.hasOutroMethod)}, ${create_each_block}, "${mountOrIntro}", ${anchor}, function(#i) { ${blocks} = @updateKeyedEach(${blocks}, #component, changed, ${get_key}, ${dynamic ? '1' : '0'}, ${each_block_value}, ${lookup}, ${updateMountNode}, ${String(this.block.hasOutroMethod)}, ${create_each_block}, "${mountOrIntro}", ${anchor}, function(#i) {
return @assign(@assign({}, ctx), { return @assign(@assign({}, ctx), {
${this.contextProps.join(',\n')} ${this.contextProps.join(',\n')}
}); });

@ -299,23 +299,7 @@ export default function mustache(parser: Parser) {
if (parser.eat('(')) { if (parser.eat('(')) {
parser.allowWhitespace(); parser.allowWhitespace();
const expression = readExpression(parser); block.key = readExpression(parser);
// TODO eventually, we should accept any expression, and turn
// it into a function. For now, assume that every expression
// follows the `foo.id` pattern, and equates to `@id`
if (
expression.type !== 'MemberExpression' ||
expression.property.computed ||
expression.property.type !== 'Identifier'
) {
parser.error({
code: `invalid-key`,
message: 'invalid key'
}, expression.start);
}
block.key = expression.property.name;
parser.allowWhitespace(); parser.allowWhitespace();
parser.eat(')', true); parser.eat(')', true);
parser.allowWhitespace(); parser.allowWhitespace();

@ -10,7 +10,7 @@ export function outroAndDestroyBlock(block, lookup) {
}); });
} }
export function updateKeyedEach(old_blocks, component, changed, key_prop, dynamic, list, lookup, node, has_outro, create_each_block, intro_method, next, get_context) { export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic, list, lookup, node, has_outro, create_each_block, intro_method, next, get_context) {
var o = old_blocks.length; var o = old_blocks.length;
var n = list.length; var n = list.length;
@ -24,14 +24,15 @@ export function updateKeyedEach(old_blocks, component, changed, key_prop, dynami
var i = n; var i = n;
while (i--) { while (i--) {
var key = list[i][key_prop]; var ctx = get_context(i);
var key = get_key(ctx);
var block = lookup[key]; var block = lookup[key];
if (!block) { if (!block) {
block = create_each_block(component, key, get_context(i)); block = create_each_block(component, key, ctx);
block.c(); block.c();
} else if (dynamic) { } else if (dynamic) {
block.p(changed, get_context(i)); block.p(changed, ctx);
} }
new_blocks[i] = new_lookup[key] = block; new_blocks[i] = new_lookup[key] = block;

@ -1,5 +1,4 @@
{ {
"hash": "1x6az5m",
"html": { "html": {
"start": 0, "start": 0,
"end": 54, "end": 54,
@ -38,7 +37,24 @@
} }
], ],
"context": "todo", "context": "todo",
"key": "id" "key": {
"type": "MemberExpression",
"start": 22,
"end": 29,
"object": {
"type": "Identifier",
"start": 22,
"end": 26,
"name": "todo"
},
"property": {
"type": "Identifier",
"start": 27,
"end": 29,
"name": "id"
},
"computed": false
}
} }
] ]
}, },

@ -0,0 +1,31 @@
export default {
data: {
words: ['foo', 'bar', 'baz']
},
html: `
<p>foo</p>
<p>bar</p>
<p>baz</p>
`,
test(assert, component, target) {
const [p1, p2, p3] = target.querySelectorAll('p');
component.set({
words: ['foo', 'baz'],
});
assert.htmlEqual(target.innerHTML, `
<p>foo</p>
<p>baz</p>
`);
const [p4, p5] = target.querySelectorAll('p');
assert.ok(!target.contains(p2), '<p> element should be removed');
assert.equal(p1, p4, 'first <p> element should be retained');
assert.equal(p3, p5, 'last <p> element should be retained');
},
};

@ -0,0 +1,3 @@
{#each words as word (word)}
<p>{word}</p>
{/each}
Loading…
Cancel
Save