Repository navigation
fix(core): only set input on specific instance in ComponentRef.setInput #46997
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,7 +29,7 @@ import {diPublicInInjector, getOrCreateNodeInjectorForNode, NodeInjector} from ' | |
| import {throwProviderNotFoundError} from './errors_di'; | ||
| import {registerPostOrderHooks} from './hooks'; | ||
| import {reportUnknownPropertyError} from './instructions/element_validation'; | ||
| import {addToViewTree, CLEAN_PROMISE, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, markDirtyIfOnPush, registerHostBindingOpCodes, renderView, setInputsForProperty} from './instructions/shared'; | ||
| import {addToViewTree, CLEAN_PROMISE, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, markDirtyIfOnPush, registerHostBindingOpCodes, renderView, setInput} from './instructions/shared'; | ||
| import {ComponentDef, RenderFlags} from './interfaces/definition'; | ||
| import {PropertyAliasValue, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; | ||
| import {PlayerHandler} from './interfaces/player'; | ||
|
|
@@ -288,17 +288,21 @@ export class ComponentRef<T> extends AbstractComponentRef<T> { | |
| let dataValue: PropertyAliasValue|undefined; | ||
| if (inputData !== null && (dataValue = inputData[name])) { | ||
| const lView = this._rootLView; | ||
| setInputsForProperty(lView[TVIEW], lView, dataValue, name, value); | ||
| markDirtyIfOnPush(lView, this._tNode.index); | ||
| } else { | ||
| if (ngDevMode) { | ||
| const cmpNameForError = stringifyForError(this.componentType); | ||
| let message = | ||
| `Can't set value of the '${name}' input on the '${cmpNameForError}' component. `; | ||
| message += `Make sure that the '${ | ||
| name}' property is annotated with @Input() or a mapped @Input('${name}') exists.`; | ||
| reportUnknownPropertyError(message); | ||
| for (let i = 0; i < dataValue.length; i += 2) { | ||
| const index = dataValue[i] as number; | ||
| if (this._rootLView[index] === this.instance) { | ||
| setInput(lView[TVIEW], this.instance, index, name, dataValue[i + 1] as string, value); | ||
| markDirtyIfOnPush(lView, this._tNode.index); | ||
| break; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||
| } | ||
| } | ||
| } else if (ngDevMode) { | ||
| const cmpNameForError = stringifyForError(this.componentType); | ||
| let message = | ||
| `Can't set value of the '${name}' input on the '${cmpNameForError}' component. `; | ||
| message += `Make sure that the '${ | ||
| name}' property is annotated with @Input() or a mapped @Input('${name}') exists.`; | ||
| reportUnknownPropertyError(message); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -435,7 +439,7 @@ export function createRootComponentView( | |
| * Creates a root component and sets it up with features and host bindings.Shared by | ||
| * renderComponent() and ViewContainerRef.createComponent(). | ||
| */ | ||
| export function createRootComponent<T>( | ||
| function createRootComponent<T>( | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated change to the rest of the fix, I just saw that this function is only used within the file.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: could we move those refactor changes to a separate (refactor) commit? |
||
| componentView: LView, componentDef: ComponentDef<T>, rootLView: LView, rootContext: RootContext, | ||
| hostFeatures: HostFeature[]|null): any { | ||
| const tView = rootLView[TVIEW]; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1992,17 +1992,33 @@ export function handleError(lView: LView, error: any): void { | |
| */ | ||
| export function setInputsForProperty( | ||
| tView: TView, lView: LView, inputs: PropertyAliasValue, publicName: string, value: any): void { | ||
| for (let i = 0; i < inputs.length;) { | ||
| const index = inputs[i++] as number; | ||
| const privateName = inputs[i++] as string; | ||
| for (let i = 0; i < inputs.length; i += 2) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is also unrelated to the fix, but I think that it's more readable like this compared to incrementing
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: could we move those refactor changes to a separate (refactor) commit? |
||
| const index = inputs[i] as number; | ||
| const privateName = inputs[i + 1] as string; | ||
| const instance = lView[index]; | ||
| ngDevMode && assertIndexInRange(lView, index); | ||
| const def = tView.data[index] as DirectiveDef<any>; | ||
| if (def.setInput !== null) { | ||
| def.setInput!(instance, value, publicName, privateName); | ||
| } else { | ||
| instance[privateName] = value; | ||
| } | ||
| setInput(tView, instance, index, publicName, privateName, value); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Sets a single input on a directive instance. | ||
| * | ||
| * @param tView Current TView. | ||
| * @param instance Directive instance to which to write the input. | ||
| * @param index Index at which to find the directive's defintion in the TView. | ||
| * @param publicName Public name of the input. | ||
| * @param privateName Private name of the input. | ||
| * @param value New value that should be assigned. | ||
| */ | ||
| export function setInput( | ||
| tView: TView, instance: any, index: number, publicName: string, privateName: string, | ||
| value: unknown) { | ||
| const def = tView.data[index] as DirectiveDef<any>; | ||
| if (def.setInput !== null) { | ||
| def.setInput(instance, value, publicName, privateName); | ||
| } else { | ||
| instance[privateName] = value; | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Figuring out which inputs belong to this instance like this felt a little weird, but the only alternative would be to look at the index and I couldn't find a good way of getting the index of the component instance within the root
LView.