diff --git a/src/generators/dom/visitors/attributes/addComponentBinding.js b/src/generators/dom/visitors/attributes/addComponentBinding.js index 3b3f4bb2aea2..f7ca07a10696 100644 --- a/src/generators/dom/visitors/attributes/addComponentBinding.js +++ b/src/generators/dom/visitors/attributes/addComponentBinding.js @@ -1,77 +1,41 @@ import deindent from '../../../../utils/deindent.js'; -import isReference from '../../../../utils/isReference.js'; import flattenReference from '../../../../utils/flattenReference.js'; +import getSetter from './binding/getSetter.js'; export default function createBinding ( generator, node, attribute, current, local ) { - const { name, parts, keypath } = flattenReference( attribute.value ); + const { name } = flattenReference( attribute.value ); + const { snippet, contexts, dependencies } = generator.contextualise( attribute.value ); - const contextual = name in current.contexts; + if ( dependencies.length > 1 ) throw new Error( 'An unexpected situation arose. Please raise an issue at https://github.com/sveltejs/svelte/issues — thanks!' ); - if ( contextual && !~local.allUsedContexts.indexOf( name ) ) { - local.allUsedContexts.push( name ); - } + contexts.forEach( context => { + if ( !~local.allUsedContexts.indexOf( context ) ) local.allUsedContexts.push( context ); + }); + + const contextual = name in current.contexts; let obj; let prop; - let value; if ( contextual ) { obj = current.listNames[ name ]; prop = current.indexNames[ name ]; - value = keypath; + } else if ( attribute.value.type === 'MemberExpression' ) { + prop = `'[✂${attribute.value.property.start}-${attribute.value.property.end}✂]}'`; + obj = `root.[✂${attribute.value.object.start}-${attribute.value.object.end}✂]}`; } else { - prop = `'${parts.slice( -1 )}'`; - obj = parts.length > 1 ? `root.${parts.slice( 0, -1 ).join( '.' )}` : `root`; - value = `root.${keypath}`; + obj = 'root'; + prop = `'${name}'`; } - local.bindings.push({ name: attribute.name, value, obj, prop }); - - let setter; - - if ( contextual ) { - // find the top-level property that this is a child of - let fragment = current; - let prop = name; - - do { - if ( fragment.expression && fragment.context === prop ) { - if ( !isReference( fragment.expression ) ) { - // TODO this should happen in prior validation step - throw new Error( `${prop} is read-only, it cannot be bound` ); - } - - prop = flattenReference( fragment.expression ).name; - } - } while ( fragment = fragment.parent ); + local.bindings.push({ + name: attribute.name, + value: snippet, + obj, + prop + }); - generator.expectedProperties[ prop ] = true; - - const listName = current.listNames[ name ]; - const indexName = current.indexNames[ name ]; - - const context = local.isComponent ? `_context` : `__svelte`; - - setter = deindent` - var list = this.${context}.${listName}; - var index = this.${context}.${indexName}; - list[index]${parts.slice( 1 ).map( part => `.${part}` ).join( '' )} = value; - - component._set({ ${prop}: component.get( '${prop}' ) }); - `; - } else { - if ( parts.length > 1 ) { - setter = deindent` - var ${name} = component.get( '${name}' ); - ${name}.${parts.slice( 1 ).join( '.' )} = value; - component._set({ ${name}: ${name} }); - `; - } else { - setter = `component._set({ ${keypath}: value });`; - } - - generator.expectedProperties[ name ] = true; - } + const setter = getSetter({ current, name, context: '_context', attribute, dependencies, snippet, value: 'value' }); generator.hasComplexBindings = true; @@ -88,11 +52,9 @@ export default function createBinding ( generator, node, attribute, current, loc }); ` ); - const dependencies = name in current.contexts ? current.contextDependencies[ name ] : [ name ]; - local.update.addBlock( deindent` if ( !${local.name}_updating && ${dependencies.map( dependency => `'${dependency}' in changed` ).join( '||' )} ) { - ${local.name}._set({ ${attribute.name}: ${contextual ? keypath : `root.${keypath}`} }); + ${local.name}._set({ ${attribute.name}: ${snippet} }); } ` ); } diff --git a/src/generators/dom/visitors/attributes/addElementBinding.js b/src/generators/dom/visitors/attributes/addElementBinding.js index 2965774c6c2d..1d0fb73de177 100644 --- a/src/generators/dom/visitors/attributes/addElementBinding.js +++ b/src/generators/dom/visitors/attributes/addElementBinding.js @@ -1,5 +1,6 @@ import deindent from '../../../../utils/deindent.js'; import flattenReference from '../../../../utils/flattenReference.js'; +import getSetter from './binding/getSetter.js'; export default function createBinding ( generator, node, attribute, current, local ) { const { name } = flattenReference( attribute.value ); @@ -17,30 +18,7 @@ export default function createBinding ( generator, node, attribute, current, loc const value = getBindingValue( local, node, attribute, isMultipleSelect ); const eventName = getBindingEventName( node ); - let setter; - - if ( name in current.contexts ) { - const prop = dependencies[0]; - const tail = attribute.value.type === 'MemberExpression' ? getTailSnippet( attribute.value ) : ''; - - setter = deindent` - var list = this.__svelte.${current.listNames[ name ]}; - var index = this.__svelte.${current.indexNames[ name ]}; - list[index]${tail} = ${value}; - - component._set({ ${prop}: component.get( '${prop}' ) }); - `; - } else { - if ( attribute.value.type === 'MemberExpression' ) { - setter = deindent` - var ${name} = component.get( '${name}' ); - ${snippet} = ${value}; - component._set({ ${name}: ${name} }); - `; - } else { - setter = `component._set({ ${name}: ${value} });`; - } - } + let setter = getSetter({ current, name, context: '__svelte', attribute, dependencies, snippet, value }); // special case if ( node.name === 'select' && !isMultipleSelect ) { diff --git a/src/generators/dom/visitors/attributes/binding/getSetter.js b/src/generators/dom/visitors/attributes/binding/getSetter.js new file mode 100644 index 000000000000..64c822df7c14 --- /dev/null +++ b/src/generators/dom/visitors/attributes/binding/getSetter.js @@ -0,0 +1,34 @@ +import deindent from '../../../../../utils/deindent.js'; + +export default function getSetter ({ current, name, context, attribute, dependencies, snippet, value }) { + if ( name in current.contexts ) { + const prop = dependencies[0]; + const tail = attribute.value.type === 'MemberExpression' ? getTailSnippet( attribute.value ) : ''; + + return deindent` + var list = this.${context}.${current.listNames[ name ]}; + var index = this.${context}.${current.indexNames[ name ]}; + list[index]${tail} = ${value}; + + component._set({ ${prop}: component.get( '${prop}' ) }); + `; + } + + if ( attribute.value.type === 'MemberExpression' ) { + return deindent` + var ${name} = component.get( '${name}' ); + ${snippet} = ${value}; + component._set({ ${name}: ${name} }); + `; + } + + return `component._set({ ${name}: ${value} });`; +} + +function getTailSnippet ( node ) { + const end = node.end; + while ( node.type === 'MemberExpression' ) node = node.object; + const start = node.end; + + return `[✂${start}-${end}✂]`; +} \ No newline at end of file