diff --git a/compiler/generate/index.js b/compiler/generate/index.js index 02477cb38d..0ed0ed65da 100644 --- a/compiler/generate/index.js +++ b/compiler/generate/index.js @@ -521,6 +521,7 @@ export default function generate ( parsed, source, options ) { }; this.root = options.root; + this.yield = options.yield; ${initStatements.join( '\n\n' )} } diff --git a/compiler/generate/visitors/Element.js b/compiler/generate/visitors/Element.js index c928a395a1..2aee0eac26 100644 --- a/compiler/generate/visitors/Element.js +++ b/compiler/generate/visitors/Element.js @@ -1,9 +1,11 @@ import deindent from '../utils/deindent.js'; import addComponentAttributes from './attributes/addComponentAttributes.js'; import addElementAttributes from './attributes/addElementAttributes.js'; +import counter from '../utils/counter.js'; export default { enter ( generator, node ) { + const hasChildren = node.children.length > 0; const isComponent = node.name in generator.components; const name = generator.current.counter( isComponent ? `${node.name[0].toLowerCase()}${node.name.slice( 1 )}` : node.name ); @@ -28,13 +30,49 @@ export default { addComponentAttributes( generator, node, local ); + const componentInitProperties = [ + `target: ${!isToplevel ? generator.current.target: 'null'}`, + 'root: component.root || component' + ]; + // Component has children + if ( hasChildren ) { + const yieldName = `render${name}YieldFragment`; + + // {{YIELD STUFF}} + generator.push({ + useAnchor: true, + name: generator.current.counter(yieldName), + target: 'target', + localElementDepth: 0, + + initStatements: [], + mountStatements: [], + updateStatements: [], + teardownStatements: [], + + counter: counter() + }); + + node.children.forEach( generator.visit ); + generator.addRenderer( generator.current ); + generator.pop(); + + // Don't render children twice + node.children = []; + + generator.current.initStatements.push(`var ${name}_yieldFragment = ${yieldName}( root, component );`); + generator.current.updateStatements.push(`${name}_yieldFragment.update ( changed, root );`); + + componentInitProperties.push(`yield: ${name}_yieldFragment`); + } + + const statements = []; + if ( local.staticAttributes.length || local.dynamicAttributes.length || local.bindings.length ) { const initialProps = local.staticAttributes .concat( local.dynamicAttributes ) .map( attribute => `${attribute.name}: ${attribute.value}` ); - const statements = []; - if ( initialProps.length ) { statements.push( deindent` var ${name}_initialData = { @@ -54,24 +92,17 @@ export default { statements.push( bindings.join( '\n' ) ); } + componentInitProperties.push(`data: ${name}_initialData`); + } - local.init.unshift( deindent` - ${statements.join( '\n\n' )} + local.init.unshift( deindent` + ${statements.join( '\n\n' )} + + var ${name} = new template.components.${node.name}({ + ${componentInitProperties.join(',\n')} + }); + ` ); - var ${name} = new template.components.${node.name}({ - target: ${!isToplevel ? generator.current.target: 'null'}, - root: component.root || component, - data: ${name}_initialData - }); - ` ); - } else { - local.init.unshift( deindent` - var ${name} = new template.components.${node.name}({ - target: ${!isToplevel ? generator.current.target: 'null'}, - root: component.root || component - }); - ` ); - } if ( isToplevel ) { local.mount.unshift( `${name}.mount( target, anchor );` ); diff --git a/compiler/generate/visitors/YieldTag.js b/compiler/generate/visitors/YieldTag.js new file mode 100644 index 0000000000..5ec8727ad1 --- /dev/null +++ b/compiler/generate/visitors/YieldTag.js @@ -0,0 +1,7 @@ +export default { + enter ( generator ) { + const anchor = generator.createAnchor( 'yield', 'yield' ); + generator.current.mountStatements.push(`component.yield && component.yield.mount( ${generator.current.target}, ${anchor} );`); + generator.current.teardownStatements.push(`component.yield && component.yield.teardown( detach );`); + } +}; diff --git a/compiler/generate/visitors/index.js b/compiler/generate/visitors/index.js index 064669762f..70ba9e3298 100644 --- a/compiler/generate/visitors/index.js +++ b/compiler/generate/visitors/index.js @@ -4,6 +4,7 @@ import Element from './Element.js'; import IfBlock from './IfBlock.js'; import MustacheTag from './MustacheTag.js'; import Text from './Text.js'; +import YieldTag from './YieldTag.js'; export default { Comment, @@ -11,5 +12,6 @@ export default { Element, IfBlock, MustacheTag, - Text + Text, + YieldTag }; diff --git a/compiler/parse/state/mustache.js b/compiler/parse/state/mustache.js index e412b1043f..b27e694608 100644 --- a/compiler/parse/state/mustache.js +++ b/compiler/parse/state/mustache.js @@ -177,6 +177,19 @@ export default function mustache ( parser ) { parser.stack.push( block ); } + // {{yield}} + else if ( parser.eat( 'yield' ) ) { + + parser.allowWhitespace(); + parser.eat( '}}', true ); + + parser.current().children.push({ + start, + end: parser.index, + type: 'YieldTag' + }); + } + else { const expression = readExpression( parser ); diff --git a/test/compiler/component-yield-if/Widget.html b/test/compiler/component-yield-if/Widget.html new file mode 100644 index 0000000000..7dbf91afb5 --- /dev/null +++ b/test/compiler/component-yield-if/Widget.html @@ -0,0 +1,15 @@ +

+ {{#if show}} + {{yield}} + {{/if}} +

+ diff --git a/test/compiler/component-yield-if/_config.js b/test/compiler/component-yield-if/_config.js new file mode 100644 index 0000000000..fad768230d --- /dev/null +++ b/test/compiler/component-yield-if/_config.js @@ -0,0 +1,13 @@ +export default { + html: '

', + test ( assert, component, target ) { + const widget = component.refs.widget; + assert.equal( widget.get( 'show' ), false ); + widget.set({show: true}); + assert.equal( target.innerHTML, '

Hello

' ); + component.set({data: 'World'}); + assert.equal( target.innerHTML, '

World

' ); + widget.set({show: false}); + assert.equal( target.innerHTML, '

' ); + } +} diff --git a/test/compiler/component-yield-if/main.html b/test/compiler/component-yield-if/main.html new file mode 100644 index 0000000000..61f0909984 --- /dev/null +++ b/test/compiler/component-yield-if/main.html @@ -0,0 +1,14 @@ +
+ {{data}} +
+ diff --git a/test/compiler/component-yield-parent/Widget.html b/test/compiler/component-yield-parent/Widget.html new file mode 100644 index 0000000000..e147954fa0 --- /dev/null +++ b/test/compiler/component-yield-parent/Widget.html @@ -0,0 +1 @@ +

{{yield}}

diff --git a/test/compiler/component-yield-parent/_config.js b/test/compiler/component-yield-parent/_config.js new file mode 100644 index 0000000000..28b8e175de --- /dev/null +++ b/test/compiler/component-yield-parent/_config.js @@ -0,0 +1,9 @@ +export default { + html: '

Hello

', + test ( assert, component, target ) { + assert.equal( component.get( 'data' ), 'Hello' ); + component.set({data: 'World'}) + assert.equal( component.get( 'data' ), 'World' ); + assert.equal( target.innerHTML, '

World

' ); + } +} diff --git a/test/compiler/component-yield-parent/main.html b/test/compiler/component-yield-parent/main.html new file mode 100644 index 0000000000..8a043a6ca7 --- /dev/null +++ b/test/compiler/component-yield-parent/main.html @@ -0,0 +1,14 @@ +
+ {{data}} +
+ diff --git a/test/compiler/component-yield/_config.js b/test/compiler/component-yield/_config.js new file mode 100644 index 0000000000..fcf00b5072 --- /dev/null +++ b/test/compiler/component-yield/_config.js @@ -0,0 +1,3 @@ +export default { + html: '

Hello

' +} diff --git a/test/compiler/component-yield/main.html b/test/compiler/component-yield/main.html new file mode 100644 index 0000000000..7d5cbf7180 --- /dev/null +++ b/test/compiler/component-yield/main.html @@ -0,0 +1,15 @@ +

+ Hello + {{#if test}} + {{yield}} + {{/if}} +

+ diff --git a/test/parser/yield/input.html b/test/parser/yield/input.html new file mode 100644 index 0000000000..889d9eeadc --- /dev/null +++ b/test/parser/yield/input.html @@ -0,0 +1 @@ +{{yield}} diff --git a/test/parser/yield/output.json b/test/parser/yield/output.json new file mode 100644 index 0000000000..227da6561f --- /dev/null +++ b/test/parser/yield/output.json @@ -0,0 +1,14 @@ +{ + "html": { + "start": 0, + "end": 9, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 9, + "type": "YieldTag" + } + ] + } +}