From 3235b2c35adb0d58d21884e89b2f206a2c0dc2a9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 31 Jan 2017 16:29:57 -0500 Subject: [PATCH] always flush render hooks, even if initiator is a nested component (#263) --- src/generators/dom/index.js | 33 ++++++++++--------- .../dom/visitors/attributes/binding/index.js | 4 +-- src/shared/methods.js | 14 ++++++++ test/generator/onrender-chain/Item.html | 16 +++++++++ test/generator/onrender-chain/List.html | 25 ++++++++++++++ test/generator/onrender-chain/_config.js | 15 +++++++++ test/generator/onrender-chain/main.html | 11 +++++++ 7 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 test/generator/onrender-chain/Item.html create mode 100644 test/generator/onrender-chain/List.html create mode 100644 test/generator/onrender-chain/_config.js create mode 100644 test/generator/onrender-chain/main.html diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index de0c5dc496..649252557d 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -174,11 +174,11 @@ export default function dom ( parsed, source, options, names ) { const builders = { main: new CodeBuilder(), init: new CodeBuilder(), - set: new CodeBuilder() + _set: new CodeBuilder() }; - builders.set.addLine( 'var oldState = this._state;' ); - builders.set.addLine( 'this._state = Object.assign( {}, oldState, newState );' ); + builders._set.addLine( 'var oldState = this._state;' ); + builders._set.addLine( 'this._state = Object.assign( {}, oldState, newState );' ); if ( computations.length ) { const builder = new CodeBuilder(); @@ -197,11 +197,11 @@ export default function dom ( parsed, source, options, names ) { } ` ); - builders.set.addLine( `applyComputations( this._state, newState, oldState )` ); + builders._set.addLine( `applyComputations( this._state, newState, oldState )` ); } // TODO is the `if` necessary? - builders.set.addBlock( deindent` + builders._set.addBlock( deindent` dispatchObservers( this, this._observers.pre, newState, oldState ); if ( this._fragment ) this._fragment.update( newState, this._state ); dispatchObservers( this, this._observers.post, newState, oldState ); @@ -246,7 +246,7 @@ export default function dom ( parsed, source, options, names ) { while ( this._bindings.length ) this._bindings.pop()(); ` ); - builders.set.addLine( `while ( this._bindings.length ) this._bindings.pop()();` ); + builders._set.addLine( `while ( this._bindings.length ) this._bindings.pop()();` ); } else { builders.init.addBlock( deindent` this._fragment = renderMainFragment( this._state, this ); @@ -255,15 +255,10 @@ export default function dom ( parsed, source, options, names ) { } if ( generator.hasComponents ) { - const statement = deindent` - while ( this._renderHooks.length ) { - var hook = this._renderHooks.pop(); - hook.fn.call( hook.context ); - } - `; + const statement = `this._flush();`; builders.init.addBlock( statement ); - builders.set.addBlock( statement ); + builders._set.addBlock( statement ); } if ( templateProperties.onrender ) { @@ -310,6 +305,8 @@ export default function dom ( parsed, source, options, names ) { ${name}.prototype.fire = fire; ${name}.prototype.observe = observe; ${name}.prototype.on = on; + ${name}.prototype.set = set; + ${name}.prototype._flush = _flush; ` : deindent` ${name}.prototype.get = ${shared.get}; @@ -319,11 +316,15 @@ export default function dom ( parsed, source, options, names ) { ${name}.prototype.observe = ${shared.observe}; ${name}.prototype.on = ${shared.on}; + + ${name}.prototype.set = ${shared.set}; + + ${name}.prototype._flush = ${shared._flush}; ` ); builders.main.addBlock( deindent` - ${name}.prototype.set = function set ( newState ) { - ${builders.set} + ${name}.prototype._set = function _set ( newState ) { + ${builders._set} }; ${name}.prototype.teardown = function teardown ( detach ) { @@ -341,7 +342,7 @@ export default function dom ( parsed, source, options, names ) { throw new Error( `Components with shared helpers must be compiled to ES2015 modules (format: 'es')` ); } - const names = [ 'get', 'fire', 'observe', 'on', 'dispatchObservers' ].concat( Object.keys( generator.uses ) ); + const names = [ 'get', 'fire', 'observe', 'on', 'set', '_flush', 'dispatchObservers' ].concat( Object.keys( generator.uses ) ); builders.main.addLineAtStart( `import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )}` ); diff --git a/src/generators/dom/visitors/attributes/binding/index.js b/src/generators/dom/visitors/attributes/binding/index.js index a546d2fa97..1ed1dde6e5 100644 --- a/src/generators/dom/visitors/attributes/binding/index.js +++ b/src/generators/dom/visitors/attributes/binding/index.js @@ -64,7 +64,7 @@ export default function createBinding ( generator, node, attribute, current, loc var index = this.__svelte.${indexName}; list[index]${parts.slice( 1 ).map( part => `.${part}` ).join( '' )} = ${value}; - component.set({ ${prop}: component.get( '${prop}' ) }); + component._set({ ${prop}: component.get( '${prop}' ) }); `; } else if ( deep ) { setter = deindent` @@ -98,7 +98,7 @@ export default function createBinding ( generator, node, attribute, current, loc local.update.addBlock( deindent` if ( !${local.name}_updating && '${parts[0]}' in changed ) { - ${local.name}.set({ ${attribute.name}: ${contextual ? attribute.value : `root.${attribute.value}`} }); + ${local.name}._set({ ${attribute.name}: ${contextual ? attribute.value : `root.${attribute.value}`} }); } ` ); } else { diff --git a/src/shared/methods.js b/src/shared/methods.js index 4595a13523..34bcdac498 100644 --- a/src/shared/methods.js +++ b/src/shared/methods.js @@ -41,3 +41,17 @@ export function on ( eventName, handler ) { } }; } + +export function set ( newState ) { + this._set( newState ); + ( this._root || this )._flush(); +} + +export function _flush () { + if ( !this._renderHooks ) return; + + while ( this._renderHooks.length ) { + var hook = this._renderHooks.pop(); + hook.fn.call( hook.context ); + } +} diff --git a/test/generator/onrender-chain/Item.html b/test/generator/onrender-chain/Item.html new file mode 100644 index 0000000000..d039a445f1 --- /dev/null +++ b/test/generator/onrender-chain/Item.html @@ -0,0 +1,16 @@ +{{foo}} + + diff --git a/test/generator/onrender-chain/List.html b/test/generator/onrender-chain/List.html new file mode 100644 index 0000000000..5bf8f12cd8 --- /dev/null +++ b/test/generator/onrender-chain/List.html @@ -0,0 +1,25 @@ +{{#each items as item}} + +{{/each}} + + diff --git a/test/generator/onrender-chain/_config.js b/test/generator/onrender-chain/_config.js new file mode 100644 index 0000000000..348f66cd6f --- /dev/null +++ b/test/generator/onrender-chain/_config.js @@ -0,0 +1,15 @@ +export default { + html: ` + 321 + `, + + test ( assert, component, target ) { + component.refs.list.update(); + + assert.htmlEqual( target.innerHTML, ` + 12345 + ` ); + + component.teardown(); + } +}; diff --git a/test/generator/onrender-chain/main.html b/test/generator/onrender-chain/main.html new file mode 100644 index 0000000000..90dc4c94d9 --- /dev/null +++ b/test/generator/onrender-chain/main.html @@ -0,0 +1,11 @@ + + +