Skip to content

Commit 780774f

Browse files
aparziAndrewKushnir
authored andcommitted
fix(core): InputBinding marks component a dirty. (#62613)
This fix ensures that CD runs on OnPush component when an input binding is set. PR Close #62613
1 parent 1408baf commit 780774f

File tree

2 files changed

+35
-0
lines changed

2 files changed

+35
-0
lines changed

packages/core/src/render3/dynamic_bindings.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import {TVIEW} from './interfaces/view';
1616
import {getCurrentTNode, getLView, getSelectedTNode, nextBindingIndex} from './state';
1717
import {stringifyForError} from './util/stringify_utils';
1818
import {createOutputListener} from './view/directive_outputs';
19+
import {markViewDirty} from './instructions/mark_view_dirty';
20+
import {getComponentLViewByIndex} from './util/view_utils';
21+
import {NotificationSource} from '../change_detection/scheduling/zoneless_scheduling';
1922

2023
/** Symbol used to store and retrieve metadata about a binding. */
2124
export const BINDING: unique symbol = /* @__PURE__ */ Symbol('BINDING');
@@ -70,6 +73,9 @@ function inputBindingUpdate(targetDirectiveIdx: number, publicName: string, valu
7073
const tView = lView[TVIEW];
7174
const tNode = getSelectedTNode();
7275

76+
const componentLView = getComponentLViewByIndex(tNode.index, lView);
77+
markViewDirty(componentLView, NotificationSource.SetInput);
78+
7379
// TODO(pk): don't check on each and every binding, just assert in dev mode
7480
const targetDef = tView.directiveRegistry![targetDirectiveIdx];
7581
if (ngDevMode && !targetDef) {

packages/core/test/acceptance/create_component_spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
} from '../../src/core';
3737
import {stringifyForError} from '../../src/render3/util/stringify_utils';
3838
import {TestBed} from '../../testing';
39+
import {ChangeDetectionStrategy} from '@angular/compiler';
3940

4041
describe('createComponent', () => {
4142
it('should create an instance of a standalone component', () => {
@@ -1111,6 +1112,34 @@ describe('createComponent', () => {
11111112
/Cannot call `setInput` on a component that is using the `inputBinding` or `twoWayBinding` functions/,
11121113
);
11131114
});
1115+
1116+
it('should update view of component set with the onPush strategy after input change', () => {
1117+
@Component({
1118+
standalone: true,
1119+
changeDetection: ChangeDetectionStrategy.OnPush,
1120+
template: 'Value: {{ value }}',
1121+
})
1122+
class DisplayOnPushComponent {
1123+
@Input() value: number = -1;
1124+
}
1125+
1126+
const hostElement = document.createElement('div');
1127+
const environmentInjector = TestBed.inject(EnvironmentInjector);
1128+
const valueSignal = signal(10);
1129+
1130+
const componentRef = createComponent(DisplayOnPushComponent, {
1131+
hostElement,
1132+
environmentInjector,
1133+
bindings: [inputBinding('value', valueSignal)],
1134+
});
1135+
1136+
componentRef.changeDetectorRef.detectChanges();
1137+
expect(hostElement.textContent).toBe('Value: 10');
1138+
1139+
valueSignal.set(20);
1140+
componentRef.changeDetectorRef.detectChanges();
1141+
expect(hostElement.textContent).toBe('Value: 20');
1142+
});
11141143
});
11151144

11161145
describe('root component outputs', () => {

0 commit comments

Comments
 (0)