From fec55a1ccd0f1142b4d1268c53e45ea00c27918c Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 5 Dec 2016 09:29:44 +0100 Subject: [PATCH 1/2] add each-else support to parser --- compiler/parse/state/mustache.js | 4 +- test/parser/each-block-else/input.html | 5 ++ test/parser/each-block-else/output.json | 67 +++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/parser/each-block-else/input.html create mode 100644 test/parser/each-block-else/output.json diff --git a/compiler/parse/state/mustache.js b/compiler/parse/state/mustache.js index b27e694608..98d371b418 100644 --- a/compiler/parse/state/mustache.js +++ b/compiler/parse/state/mustache.js @@ -112,7 +112,9 @@ export default function mustache ( parser ) { else if ( parser.eat( 'else' ) ) { const block = parser.current(); - if ( block.type !== 'IfBlock' ) parser.error( 'Cannot have an {{else}} block outside an {{#if ...}} block' ); + if ( block.type !== 'IfBlock' && block.type !== 'EachBlock' ) { + parser.error( 'Cannot have an {{else}} block outside an {{#if ...}} or {{#each ...}} block' ); + } parser.allowWhitespace(); parser.eat( '}}', true ); diff --git a/test/parser/each-block-else/input.html b/test/parser/each-block-else/input.html new file mode 100644 index 0000000000..59925e8db7 --- /dev/null +++ b/test/parser/each-block-else/input.html @@ -0,0 +1,5 @@ +{{#each animals as animal}} +

{{animal}}

+{{else}} +

no animals

+{{/each}} diff --git a/test/parser/each-block-else/output.json b/test/parser/each-block-else/output.json new file mode 100644 index 0000000000..8d82d5a677 --- /dev/null +++ b/test/parser/each-block-else/output.json @@ -0,0 +1,67 @@ +{ + "html": { + "start": 0, + "end": 84, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 84, + "type": "EachBlock", + "expression": { + "start": 8, + "end": 15, + "type": "Identifier", + "name": "animals" + }, + "context": "animal", + "children": [ + { + "start": 29, + "end": 46, + "type": "Element", + "name": "p", + "attributes": [], + "children": [ + { + "start": 32, + "end": 42, + "type": "MustacheTag", + "expression": { + "start": 34, + "end": 40, + "type": "Identifier", + "name": "animal" + } + } + ] + } + ], + "else": { + "children": [ + { + "attributes": [], + "children": [ + { + "data": "no animals", + "end": 70, + "start": 60, + "type": "Text" + } + ], + "end": 74, + "name": "p", + "start": 57, + "type": "Element" + } + ], + "end": 75, + "start": 55, + "type": "ElseBlock" + } + } + ] + }, + "css": null, + "js": null +} From 5d256eb600d152baac0c5f4f1b8a2fec68d0a840 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 5 Dec 2016 09:58:39 +0100 Subject: [PATCH 2/2] add support for each-else in codegen --- compiler/generate/visitors/EachBlock.js | 68 ++++++++++++++++++++++-- test/compiler/each-block-else/_config.js | 17 ++++++ test/compiler/each-block-else/main.html | 7 +++ 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 test/compiler/each-block-else/_config.js create mode 100644 test/compiler/each-block-else/main.html diff --git a/compiler/generate/visitors/EachBlock.js b/compiler/generate/visitors/EachBlock.js index 089ab59876..845105160f 100644 --- a/compiler/generate/visitors/EachBlock.js +++ b/compiler/generate/visitors/EachBlock.js @@ -5,8 +5,11 @@ export default { enter ( generator, node ) { const i = generator.counters.each++; const name = `eachBlock_${i}`; + const elseName = `${name}_else`; const iterations = `${name}_iterations`; const renderer = `renderEachBlock_${i}`; + const renderElse = `${renderer}_else`; + const { params } = generator.current; const listName = `${name}_value`; @@ -21,12 +24,21 @@ export default { generator.current.initStatements.push( deindent` var ${name}_value = ${snippet}; var ${iterations} = []; + ${node.else ? `var ${elseName} = null;` : ''} for ( var i = 0; i < ${name}_value.length; i += 1 ) { - ${iterations}[i] = ${renderer}( ${generator.current.params}, ${listName}, ${listName}[i], i, component ); + ${iterations}[i] = ${renderer}( ${params}, ${listName}, ${listName}[i], i, component ); ${!isToplevel ? `${iterations}[i].mount( ${anchor}.parentNode, ${anchor} );` : ''} } ` ); + if ( node.else ) { + generator.current.initStatements.push( deindent` + if ( !${name}_value.length ) { + ${elseName} = ${renderElse}( ${params}, component ); + ${!isToplevel ? `${elseName}.mount( ${anchor}.parentNode, ${anchor} );` : ''} + } + ` ); + } if ( isToplevel ) { generator.current.mountStatements.push( deindent` @@ -34,6 +46,13 @@ export default { ${iterations}[i].mount( ${anchor}.parentNode, ${anchor} ); } ` ); + if ( node.else ) { + generator.current.mountStatements.push( deindent` + if ( ${elseName} ) { + ${elseName}.mount( ${anchor}.parentNode, ${anchor} ); + } + ` ); + } } generator.current.updateStatements.push( deindent` @@ -41,10 +60,10 @@ export default { for ( var i = 0; i < ${name}_value.length; i += 1 ) { if ( !${iterations}[i] ) { - ${iterations}[i] = ${renderer}( ${generator.current.params}, ${listName}, ${listName}[i], i, component ); + ${iterations}[i] = ${renderer}( ${params}, ${listName}, ${listName}[i], i, component ); ${iterations}[i].mount( ${anchor}.parentNode, ${anchor} ); } else { - ${iterations}[i].update( changed, ${generator.current.params}, ${listName}, ${listName}[i], i ); + ${iterations}[i].update( changed, ${params}, ${listName}, ${listName}[i], i ); } } @@ -54,12 +73,51 @@ export default { ${iterations}.length = ${listName}.length; ` ); + if ( node.else ) { + generator.current.updateStatements.push( deindent` + if ( !${name}_value.length && ${elseName} ) { + ${elseName}.update( changed, ${params} ); + } else if ( !${name}_value.length ) { + ${elseName} = ${renderElse}( ${params}, component ); + ${elseName}.mount( ${anchor}.parentNode, ${anchor} ); + } else if ( ${elseName} ) { + ${elseName}.teardown( true ); + } + ` ); + } generator.current.teardownStatements.push( deindent` for ( var i = 0; i < ${iterations}.length; i += 1 ) { ${iterations}[i].teardown( ${isToplevel ? 'detach' : 'false'} ); } ` ); + if ( node.else ) { + generator.current.teardownStatements.push( deindent` + if ( ${elseName} ) { + ${elseName}.teardown( ${isToplevel ? 'detach' : 'false'} ); + } + ` ); + } + + if ( node.else ) { + generator.push({ + useAnchor: false, + name: renderElse, + target: 'target', + localElementDepth: 0, + + initStatements: [], + mountStatements: [], + updateStatements: [], + detachStatements: [], + teardownStatements: [], + + counter: counter() + }); + node.else.children.forEach( generator.visit ); + generator.addRenderer( generator.current ); + generator.pop(); + } const indexNames = Object.assign( {}, generator.current.indexNames ); const indexName = indexNames[ node.context ] = ( node.index || `${node.context}__index` ); @@ -76,7 +134,7 @@ export default { const contextDependencies = Object.assign( {}, generator.current.contextDependencies ); contextDependencies[ node.context ] = dependencies; - const params = generator.current.params + `, ${listName}, ${node.context}, ${indexName}`; + const blockParams = generator.current.params + `, ${listName}, ${node.context}, ${indexName}`; generator.push({ useAnchor: false, @@ -92,7 +150,7 @@ export default { indexNames, listNames, - params, + params: blockParams, initStatements: [], mountStatements: [], diff --git a/test/compiler/each-block-else/_config.js b/test/compiler/each-block-else/_config.js new file mode 100644 index 0000000000..313b7fa7e3 --- /dev/null +++ b/test/compiler/each-block-else/_config.js @@ -0,0 +1,17 @@ +export default { + data: { + animals: [ 'alpaca', 'baboon', 'capybara' ], + foo: 'something else' + }, + html: 'before\n

alpaca

baboon

capybara

\nafter', + test ( assert, component, target ) { + component.set({ animals: [] }); + assert.htmlEqual( target.innerHTML, 'before\n

no animals, but rather something else

\nafter' ); + + component.set({ foo: 'something other' }); + assert.htmlEqual( target.innerHTML, 'before\n

no animals, but rather something other

\nafter' ); + + component.set({ animals: ['wombat'] }); + assert.htmlEqual( target.innerHTML, 'before\n

wombat

\nafter' ); + } +}; diff --git a/test/compiler/each-block-else/main.html b/test/compiler/each-block-else/main.html new file mode 100644 index 0000000000..6c13e5fb23 --- /dev/null +++ b/test/compiler/each-block-else/main.html @@ -0,0 +1,7 @@ +before +{{#each animals as animal}} +

{{animal}}

+{{else}} +

no animals, but rather {{foo}}

+{{/each}} +after