Content-Length: 488215 | pFad | https://github.com/angular/angular/commit/3459faadbfce9be7b1ca69f4d4db82a65b31de50

D3 fix(core): do not allow setInput to be used with inputBinding (#60137) · angular/angular@3459faa · GitHub
Skip to content

Commit 3459faa

Browse files
crisbetommalerba
authored andcommitted
fix(core): do not allow setInput to be used with inputBinding (#60137)
Calling `setInput` while the component already has an `inputBinding` active can lead to inconsistent state. These changes add an error that will be thrown if that's the case. PR Close #60137
1 parent fe57332 commit 3459faa

File tree

4 files changed

+42
-2
lines changed

4 files changed

+42
-2
lines changed

goldens/public-api/core/errors.api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ export const enum RuntimeErrorCode {
7979
// (undocumented)
8080
INVALID_MULTI_PROVIDER = -209,
8181
// (undocumented)
82+
INVALID_SET_INPUT_CALL = 317,
83+
// (undocumented)
8284
INVALID_SKIP_HYDRATION_HOST = -504,
8385
// (undocumented)
8486
LOOP_TRACK_DUPLICATE_KEYS = -955,

packages/core/src/errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export const enum RuntimeErrorCode {
6262
UNINITIALIZED_LET_ACCESS = 314,
6363
NO_BINDING_TARGET = 315,
6464
INVALID_BINDING_TARGET = 316,
65+
INVALID_SET_INPUT_CALL = 317,
6566

6667
// Bootstrap Errors
6768
MULTIPLE_PLATFORMS = 400,

packages/core/src/render3/component_ref.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
260260
rootViewInjector,
261261
)
262262
: createHostElement(cmpDef, hostRenderer);
263+
const hasInputBindings =
264+
componentBindings?.some(isInputBinding) ||
265+
directives?.some((d) => typeof d !== 'function' && d.bindings.some(isInputBinding));
263266

264267
const rootLView = createLView<T>(
265268
null,
@@ -355,7 +358,7 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
355358
leaveView();
356359
}
357360

358-
return new ComponentRef(this.componentType, rootLView);
361+
return new ComponentRef(this.componentType, rootLView, !!hasInputBindings);
359362
} finally {
360363
setActiveConsumer(prevConsumer);
361364
}
@@ -454,6 +457,10 @@ function getRootTViewTemplate(
454457
};
455458
}
456459

460+
function isInputBinding(binding: Binding): boolean {
461+
return binding[BINDING].kind === 'input';
462+
}
463+
457464
/**
458465
* Represents an instance of a Component created via a {@link ComponentFactory}.
459466
*
@@ -473,7 +480,8 @@ export class ComponentRef<T> extends AbstractComponentRef<T> {
473480

474481
constructor(
475482
componentType: Type<T>,
476-
private _rootLView: LView,
483+
private readonly _rootLView: LView,
484+
private readonly _hasInputBindings: boolean,
477485
) {
478486
super();
479487
this._tNode = getTNode(_rootLView[TVIEW], HEADER_OFFSET) as TElementNode;
@@ -487,6 +495,13 @@ export class ComponentRef<T> extends AbstractComponentRef<T> {
487495
}
488496

489497
override setInput(name: string, value: unknown): void {
498+
if (this._hasInputBindings && ngDevMode) {
499+
throw new RuntimeError(
500+
RuntimeErrorCode.INVALID_SET_INPUT_CALL,
501+
'Cannot call `setInput` on a component that is using the `inputBinding` function.',
502+
);
503+
}
504+
490505
const tNode = this._tNode;
491506
this.previousInputValues ??= new Map();
492507
// Do not set the input if it is the same as the last value

packages/core/test/acceptance/create_component_spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,28 @@ describe('createComponent', () => {
10841084
ref.changeDetectorRef.detectChanges();
10851085
}).toThrowError(/RootDir does not have an input with a public name of "someInput"/);
10861086
});
1087+
1088+
it('should throw when using setInput on a component already using inputBindings', () => {
1089+
@Component({template: ''})
1090+
class RootComp {
1091+
@Input() someInput = '';
1092+
}
1093+
1094+
const hostElement = document.createElement('div');
1095+
const environmentInjector = TestBed.inject(EnvironmentInjector);
1096+
const ref = createComponent(RootComp, {
1097+
hostElement,
1098+
environmentInjector,
1099+
bindings: [inputBinding('someInput', () => 'hello')],
1100+
});
1101+
ref.changeDetectorRef.detectChanges();
1102+
1103+
expect(() => {
1104+
ref.setInput('someInput', 'changed');
1105+
}).toThrowError(
1106+
/Cannot call `setInput` on a component that is using the `inputBinding` function/,
1107+
);
1108+
});
10871109
});
10881110

10891111
describe('error checking', () => {

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/angular/angular/commit/3459faadbfce9be7b1ca69f4d4db82a65b31de50

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy