deep/contextual binding

pull/31/head
Rich-Harris 9 years ago
parent 2aeaaa24b6
commit 48fb01cf7e

@ -19,11 +19,17 @@ export default function createBinding ( node, name, attribute, current, initStat
}
}
if ( deep && contextual ) {
if ( contextual ) {
// TODO can we target only things that have changed?
// TODO computed values/observers that depend on this probably won't update...
const listName = current.listNames[ parts[0] ];
const indexName = current.indexNames[ parts[0] ];
setter = deindent`
var context = this.__context.${parts[0]};
context.${parts.slice( 1 ).join( '.' )} = this.${attribute.name};
var list = this.__svelte.${listName};
var index = this.__svelte.${indexName};
list[index]${parts.slice( 1 ).map( part => `.${part}` ).join( '' )} = this.${attribute.name};
component.set({});
`;
} else if ( deep ) {
@ -32,8 +38,6 @@ export default function createBinding ( node, name, attribute, current, initStat
${parts[0]}.${parts.slice( 1 ).join( '.' )} = this.${attribute.name};
component.set({ ${parts[0]}: ${parts[0]} });
`;
} else if ( contextual ) {
throw new Error( `Reassigning context is not currently supported` );
} else {
setter = `component.set({ ${attribute.value}: ${name}.${attribute.name} });`;
}

@ -83,7 +83,11 @@ export default function generate ( parsed, template ) {
teardownStatements: [],
contexts: {},
indexes: {},
contextChain: [ 'root' ],
indexNames: {},
listNames: {},
counter: counter(),
@ -152,7 +156,7 @@ export default function generate ( parsed, template ) {
else {
// dynamic but potentially non-string attributes
contextualise( code, value.expression, current.contexts, helpers );
contextualise( code, value.expression, current.contexts, current.indexes, helpers );
result = `[✂${value.expression.start}-${value.expression.end}✂]`;
if ( metadata ) {
@ -173,7 +177,7 @@ export default function generate ( parsed, template ) {
if ( chunk.type === 'Text' ) {
return JSON.stringify( chunk.data );
} else {
contextualise( code, chunk.expression, current.contexts, helpers );
contextualise( code, chunk.expression, current.contexts, current.indexes, helpers );
return `( [✂${chunk.expression.start}-${chunk.expression.end}✂] )`;
}
}).join( ' + ' )
@ -200,7 +204,7 @@ export default function generate ( parsed, template ) {
const usedContexts = new Set();
attribute.expression.arguments.forEach( arg => {
const contexts = contextualise( code, arg, current.contexts, helpers );
const contexts = contextualise( code, arg, current.contexts, current.indexes, helpers );
contexts.forEach( context => {
usedContexts.add( context );
@ -210,10 +214,18 @@ export default function generate ( parsed, template ) {
// TODO hoist event handlers? can do `this.__component.method(...)`
if ( usedContexts.size ) {
const declarations = [...usedContexts].map( name => {
if ( name === 'root' ) return 'var root = this.__svelte.root; // 2';
const listName = current.listNames[ name ];
const indexName = current.indexNames[ name ];
return `var ${listName} = this.__svelte.${listName}, ${indexName} = this.__svelte.${indexName}, ${name} = ${listName}[${indexName}]`;
});
initStatements.push( deindent`
function ${handler} ( event ) {
var context = this.__context;
${[...usedContexts].map( name => `var ${name} = context.${name}` ).join( '\n' )}
${declarations}
[${attribute.expression.start}-${attribute.expression.end}];
}
@ -246,12 +258,19 @@ export default function generate ( parsed, template ) {
if ( allUsedContexts.size ) {
initStatements.push( deindent`
${name}.__context = {};
${name}.__svelte = {};
` );
updateStatements.push( deindent`
${[...allUsedContexts].map( contextName => `${name}.__context.${contextName} = ${contextName};` ).join( '\n' )}
` );
const declarations = [...allUsedContexts].map( contextName => {
if ( contextName === 'root' ) return `${name}.__svelte.root = root;`;
const listName = current.listNames[ contextName ];
const indexName = current.indexNames[ contextName ];
return `${name}.__svelte.${listName} = ${listName};\n${name}.__svelte.${indexName} = ${indexName};`;
}).join( '\n' );
updateStatements.push( declarations );
}
current.initStatements.push( initStatements.join( '\n' ) );
@ -299,7 +318,7 @@ export default function generate ( parsed, template ) {
${current.target}.appendChild( ${name} );
` );
const usedContexts = contextualise( code, node.expression, current.contexts, helpers );
const usedContexts = contextualise( code, node.expression, current.contexts, current.indexes, helpers );
const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`;
if ( isReference( node.expression ) ) {
@ -338,7 +357,7 @@ export default function generate ( parsed, template ) {
var ${name} = null;
` );
const usedContexts = contextualise( code, node.expression, current.contexts, helpers );
const usedContexts = contextualise( code, node.expression, current.contexts, current.indexes, helpers );
const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`;
let expression;
@ -410,6 +429,8 @@ export default function generate ( parsed, template ) {
const name = `eachBlock_${i}`;
const renderer = `renderEachBlock_${i}`;
const listName = `${name}_value`;
current.initStatements.push( deindent`
var ${name}_anchor = document.createComment( ${JSON.stringify( `#each ${template.slice( node.expression.start, node.expression.end )}` )} );
${current.target}.appendChild( ${name}_anchor );
@ -417,7 +438,7 @@ export default function generate ( parsed, template ) {
const ${name}_fragment = document.createDocumentFragment();
` );
contextualise( code, node.expression, current.contexts, helpers );
contextualise( code, node.expression, current.contexts, current.indexes, helpers );
const snippet = `[✂${node.expression.start}-${node.expression.end}✂]`;
current.updateStatements.push( deindent`
@ -429,7 +450,7 @@ export default function generate ( parsed, template ) {
}
const iteration = ${name}_iterations[i];
${name}_iterations[i].update( ${current.contextChain.join( ', ' )}, ${name}_value[i]${node.index ? `, i` : ''} );
${name}_iterations[i].update( ${current.contextChain.join( ', ' )}, ${listName}, ${listName}[i], i );
}
for ( var i = ${name}_value.length; i < ${name}_iterations.length; i += 1 ) {
@ -437,7 +458,7 @@ export default function generate ( parsed, template ) {
}
${name}_anchor.parentNode.insertBefore( ${name}_fragment, ${name}_anchor );
${name}_iterations.length = ${name}_value.length;
${name}_iterations.length = ${listName}.length;
` );
current.teardownStatements.push( deindent`
@ -448,16 +469,19 @@ export default function generate ( parsed, template ) {
${name}_anchor.parentNode.removeChild( ${name}_anchor );
` );
const contexts = Object.assign( {}, current.contexts );
const contextChain = current.contextChain.concat( node.context );
const indexNames = Object.assign( {}, current.indexNames );
const indexName = indexNames[ node.context ] = ( node.index || `${node.context}__index` );
const listNames = Object.assign( {}, current.listNames );
listNames[ node.context ] = listName;
const contexts = Object.assign( {}, current.contexts );
contexts[ node.context ] = true;
if ( node.index ) {
// not strictly a context, but we can treat it as such
contextChain.push( node.index );
contexts[ node.index ] = true;
}
const indexes = Object.assign( {}, current.indexes );
if ( node.index ) indexes[ indexName ] = node.context;
const contextChain = current.contextChain.concat( listName, node.context, indexName );
current = {
useAnchor: false,
@ -465,10 +489,19 @@ export default function generate ( parsed, template ) {
target: 'target',
contexts,
indexes,
indexNames,
listNames,
contextChain,
initStatements: [],
updateStatements: [],
updateStatements: [ Object.keys( contexts ).map( contextName => {
const listName = listNames[ contextName ];
const indexName = indexNames[ contextName ];
return `var ${contextName} = ${listName}[${indexName}];`;
}).join( '\n' ) ],
teardownStatements: [],
counter: counter(),
@ -639,6 +672,7 @@ export default function generate ( parsed, template ) {
});
return {
code: code.toString()
code: code.toString(),
map: code.generateMap()
};
}

@ -2,7 +2,7 @@ import { walk } from 'estree-walker';
import isReference from './isReference.js';
import flattenReference from './flattenReference.js';
export default function contextualise ( code, expression, contexts, helpers ) {
export default function contextualise ( code, expression, contexts, indexes, helpers ) {
const usedContexts = [];
walk( expression, {
@ -17,6 +17,9 @@ export default function contextualise ( code, expression, contexts, helpers ) {
if ( contexts[ name ] ) {
if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name );
} else if ( indexes[ name ] ) {
const context = indexes[ name ];
if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context );
} else {
code.insertRight( node.start, `root.` );
if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' );

@ -11,17 +11,17 @@ export default {
html: `<div><input><p>one</p></div><div><input><p>two</p></div><div><input><p>three</p></div><!--#each items-->`,
test ( component, target, window ) {
const inputs = [ ...target.querySelectorAll( 'input' ) ];
const items = component.get( 'items' );
const event = new window.Event( 'input' );
assert.equal( inputs[0].value, 'one' );
const event = new window.Event( 'input' );
inputs[1].value = 'four';
inputs[1].dispatchEvent( event );
assert.equal( items[1], 'four' );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>three</p></div><!--#each items-->` );
const items = component.get( 'items' );
items[2] = 'five';
component.set({ items });

Loading…
Cancel
Save