mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
This commit fixes a compatibility bug where pre-order lifecycle hooks (onInit, doCheck, OnChanges) for directives on the same host node were executed based on the order the directives were matched, rather than the order the directives were instantiated (i.e. injection order). This discrepancy can cause issues with forms, where it is common to inject NgControl and try to extract its control property in ngOnInit. As the NgControl directive is injected, it should be instantiated before the control value accessor directive (and thus its hooks should run first). This ensures that the NgControl ngOnInit can set up the form control before the ngOnInit for the control value accessor tries to access it. Closes #32522 PR Close #34026
85 lines
3 KiB
TypeScript
85 lines
3 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {assertDefined, assertEqual, throwError} from '../util/assert';
|
|
|
|
import {getComponentDef, getNgModuleDef} from './definition';
|
|
import {TNode} from './interfaces/node';
|
|
import {isLContainer, isLView} from './interfaces/type_checks';
|
|
import {LView, TVIEW, TView} from './interfaces/view';
|
|
|
|
export function assertTNodeForLView(tNode: TNode, lView: LView) {
|
|
tNode.hasOwnProperty('tView_') && assertEqual(
|
|
(tNode as any as{tView_: TView}).tView_, lView[TVIEW],
|
|
'This TNode does not belong to this LView.');
|
|
}
|
|
|
|
export function assertComponentType(
|
|
actual: any,
|
|
msg: string = 'Type passed in is not ComponentType, it does not have \'ɵcmp\' property.') {
|
|
if (!getComponentDef(actual)) {
|
|
throwError(msg);
|
|
}
|
|
}
|
|
|
|
export function assertNgModuleType(
|
|
actual: any,
|
|
msg: string = 'Type passed in is not NgModuleType, it does not have \'ɵmod\' property.') {
|
|
if (!getNgModuleDef(actual)) {
|
|
throwError(msg);
|
|
}
|
|
}
|
|
|
|
export function assertPreviousIsParent(isParent: boolean) {
|
|
assertEqual(isParent, true, 'previousOrParentTNode should be a parent');
|
|
}
|
|
|
|
export function assertHasParent(tNode: TNode | null) {
|
|
assertDefined(tNode, 'previousOrParentTNode should exist!');
|
|
assertDefined(tNode !.parent, 'previousOrParentTNode should have a parent');
|
|
}
|
|
|
|
export function assertDataNext(lView: LView, index: number, arr?: any[]) {
|
|
if (arr == null) arr = lView;
|
|
assertEqual(
|
|
arr.length, index, `index ${index} expected to be at the end of arr (length ${arr.length})`);
|
|
}
|
|
|
|
export function assertLContainerOrUndefined(value: any): void {
|
|
value && assertEqual(isLContainer(value), true, 'Expecting LContainer or undefined or null');
|
|
}
|
|
|
|
export function assertLContainer(value: any): void {
|
|
assertDefined(value, 'LContainer must be defined');
|
|
assertEqual(isLContainer(value), true, 'Expecting LContainer');
|
|
}
|
|
|
|
export function assertLViewOrUndefined(value: any): void {
|
|
value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
|
|
}
|
|
|
|
export function assertLView(value: any) {
|
|
assertDefined(value, 'LView must be defined');
|
|
assertEqual(isLView(value), true, 'Expecting LView');
|
|
}
|
|
|
|
export function assertFirstCreatePass(tView: TView, errMessage?: string) {
|
|
assertEqual(
|
|
tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.');
|
|
}
|
|
|
|
/**
|
|
* This is a basic sanity check that an object is probably a directive def. DirectiveDef is
|
|
* an interface, so we can't do a direct instanceof check.
|
|
*/
|
|
export function assertDirectiveDef(obj: any) {
|
|
if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) {
|
|
throwError(
|
|
`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);
|
|
}
|
|
}
|