Skip to content

Commit 5552661

Browse files
benleshalxhub
authored andcommitted
refactor(ivy): revert onChanges change back to a feature (angular#28187)
- adds fixmeIvy annotation to tests that should remain updated so we can resolve those issues in the subsequent commits PR Close angular#28187
1 parent 030350f commit 5552661

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1276
-945
lines changed

packages/common/src/directives/ng_template_outlet.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,14 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChange
3434
*/
3535
@Directive({selector: '[ngTemplateOutlet]'})
3636
export class NgTemplateOutlet implements OnChanges {
37-
private _viewRef: EmbeddedViewRef<any>|null = null;
37+
// TODO(issue/24571): remove '!'.
38+
private _viewRef !: EmbeddedViewRef<any>;
3839

39-
/**
40-
* A context object to attach to the {@link EmbeddedViewRef}. This should be an
41-
* object, the object's keys will be available for binding by the local template `let`
42-
* declarations.
43-
* Using the key `$implicit` in the context object will set its value as default.
44-
*/
45-
@Input() public ngTemplateOutletContext: Object|null = null;
40+
// TODO(issue/24571): remove '!'.
41+
@Input() public ngTemplateOutletContext !: Object;
4642

47-
/**
48-
* A string defining the template reference and optionally the context object for the template.
49-
*/
50-
@Input() public ngTemplateOutlet: TemplateRef<any>|null = null;
43+
// TODO(issue/24571): remove '!'.
44+
@Input() public ngTemplateOutlet !: TemplateRef<any>;
5145

5246
constructor(private _viewContainerRef: ViewContainerRef) {}
5347

@@ -103,7 +97,7 @@ export class NgTemplateOutlet implements OnChanges {
10397

10498
private _updateExistingContext(ctx: Object): void {
10599
for (let propName of Object.keys(ctx)) {
106-
(<any>this._viewRef !.context)[propName] = (<any>this.ngTemplateOutletContext)[propName];
100+
(<any>this._viewRef.context)[propName] = (<any>this.ngTemplateOutletContext)[propName];
107101
}
108102
}
109103
}

packages/compiler-cli/src/ngtsc/annotations/src/directive.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ export function extractDirectiveMetadata(
174174
const providers: Expression|null =
175175
directive.has('providers') ? new WrappedNodeExpr(directive.get('providers') !) : null;
176176

177+
// Determine if `ngOnChanges` is a lifecycle hook defined on the component.
178+
const usesOnChanges = members.some(
179+
member => !member.isStatic && member.kind === ClassMemberKind.Method &&
180+
member.name === 'ngOnChanges');
181+
177182
// Parse exportAs.
178183
let exportAs: string[]|null = null;
179184
if (directive.has('exportAs')) {
@@ -192,6 +197,9 @@ export function extractDirectiveMetadata(
192197
const metadata: R3DirectiveMetadata = {
193198
name: clazz.name !.text,
194199
deps: getConstructorDependencies(clazz, reflector, isCore), host,
200+
lifecycle: {
201+
usesOnChanges,
202+
},
195203
inputs: {...inputsFromMeta, ...inputsFromFields},
196204
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, selector,
197205
type: new WrappedNodeExpr(clazz.name !),

packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,6 +2117,7 @@ describe('compiler compliance', () => {
21172117
selectors: [["lifecycle-comp"]],
21182118
factory: function LifecycleComp_Factory(t) { return new (t || LifecycleComp)(); },
21192119
inputs: {nameMin: ["name", "nameMin"]},
2120+
features: [$r3$.ɵNgOnChangesFeature],
21202121
consts: 0,
21212122
vars: 0,
21222123
template: function LifecycleComp_Template(rf, ctx) {},
@@ -2238,6 +2239,7 @@ describe('compiler compliance', () => {
22382239
factory: function ForOfDirective_Factory(t) {
22392240
return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef));
22402241
},
2242+
features: [$r3$.ɵNgOnChangesFeature],
22412243
inputs: {forOf: "forOf"}
22422244
});
22432245
`;
@@ -2313,6 +2315,7 @@ describe('compiler compliance', () => {
23132315
factory: function ForOfDirective_Factory(t) {
23142316
return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef));
23152317
},
2318+
features: [$r3$.ɵNgOnChangesFeature],
23162319
inputs: {forOf: "forOf"}
23172320
});
23182321
`;

packages/compiler/src/compiler_facade_interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export interface R3DirectiveMetadataFacade {
115115
queries: R3QueryMetadataFacade[];
116116
host: {[key: string]: string};
117117
propMetadata: {[key: string]: any[]};
118+
lifecycle: {usesOnChanges: boolean;};
118119
inputs: string[];
119120
outputs: string[];
120121
usesInheritance: boolean;

packages/compiler/src/render3/r3_identifiers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ export class Identifiers {
188188
static registerContentQuery:
189189
o.ExternalReference = {name: 'ɵregisterContentQuery', moduleName: CORE};
190190

191+
static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE};
192+
191193
static InheritDefinitionFeature:
192194
o.ExternalReference = {name: 'ɵInheritDefinitionFeature', moduleName: CORE};
193195

packages/compiler/src/render3/view/api.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@ export interface R3DirectiveMetadata {
7474
properties: {[key: string]: string};
7575
};
7676

77+
/**
78+
* Information about usage of specific lifecycle events which require special treatment in the
79+
* code generator.
80+
*/
81+
lifecycle: {
82+
/**
83+
* Whether the directive uses NgOnChanges.
84+
*/
85+
usesOnChanges: boolean;
86+
};
87+
7788
/**
7889
* A mapping of input field names to the property names.
7990
*/

packages/compiler/src/render3/view/compiler.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ function baseDirectiveFields(
125125
*/
126126
function addFeatures(
127127
definitionMap: DefinitionMap, meta: R3DirectiveMetadata | R3ComponentMetadata) {
128+
// e.g. `features: [NgOnChangesFeature]`
128129
const features: o.Expression[] = [];
129130

130131
const providers = meta.providers;
@@ -140,7 +141,9 @@ function addFeatures(
140141
if (meta.usesInheritance) {
141142
features.push(o.importExpr(R3.InheritDefinitionFeature));
142143
}
143-
144+
if (meta.lifecycle.usesOnChanges) {
145+
features.push(o.importExpr(R3.NgOnChangesFeature));
146+
}
144147
if (features.length) {
145148
definitionMap.set('features', o.literalArr(features));
146149
}
@@ -421,6 +424,10 @@ function directiveMetadataFromGlobalMetadata(
421424
selector: directive.selector,
422425
deps: dependenciesFromGlobalMetadata(directive.type, outputCtx, reflector),
423426
queries: queriesFromGlobalMetadata(directive.queries, outputCtx),
427+
lifecycle: {
428+
usesOnChanges:
429+
directive.type.lifecycleHooks.some(lifecycle => lifecycle == LifecycleHooks.OnChanges),
430+
},
424431
host: {
425432
attributes: directive.hostAttributes,
426433
listeners: summary.hostListeners,

packages/core/src/change_detection/change_detection_util.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ export class WrappedValue {
6464
static isWrapped(value: any): value is WrappedValue { return value instanceof WrappedValue; }
6565
}
6666

67+
/**
68+
* Represents a basic change from a previous to a new value.
69+
*
70+
* @publicApi
71+
*/
72+
export class SimpleChange {
73+
constructor(public previousValue: any, public currentValue: any, public firstChange: boolean) {}
74+
75+
/**
76+
* Check whether the new value is the first value assigned.
77+
*/
78+
isFirstChange(): boolean { return this.firstChange; }
79+
}
80+
6781
export function isListLikeIterable(obj: any): boolean {
6882
if (!isJsObject(obj)) return false;
6983
return Array.isArray(obj) ||

packages/core/src/compiler/compiler_facade_interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export interface R3DirectiveMetadataFacade {
115115
queries: R3QueryMetadataFacade[];
116116
host: {[key: string]: string};
117117
propMetadata: {[key: string]: any[]};
118+
lifecycle: {usesOnChanges: boolean;};
118119
inputs: string[];
119120
outputs: string[];
120121
usesInheritance: boolean;

packages/core/src/core_render3_private_export.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export {
2828
templateRefExtractor as ɵtemplateRefExtractor,
2929
ProvidersFeature as ɵProvidersFeature,
3030
InheritDefinitionFeature as ɵInheritDefinitionFeature,
31+
NgOnChangesFeature as ɵNgOnChangesFeature,
3132
LifecycleHooksFeature as ɵLifecycleHooksFeature,
3233
NgModuleType as ɵNgModuleType,
3334
NgModuleRef as ɵRender3NgModuleRef,

packages/core/src/interface/lifecycle_hooks.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,19 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {SimpleChanges} from './simple_change';
8+
import {SimpleChanges, SimpleChange} from './simple_change';
99

1010

11+
/**
12+
* Defines an object that associates properties with
13+
* instances of `SimpleChange`.
14+
*
15+
* @see `OnChanges`
16+
*
17+
* @publicApi
18+
*/
19+
export interface SimpleChanges { [propName: string]: SimpleChange; }
20+
1121
/**
1222
* @description
1323
* A lifecycle hook that is called when any data-bound property of a directive changes.

packages/core/src/metadata/directives.ts

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
import {ChangeDetectionStrategy} from '../change_detection/constants';
1010
import {Provider} from '../di';
1111
import {Type} from '../interface/type';
12-
import {NG_BASE_DEF, NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from '../render3/fields';
12+
import {NG_BASE_DEF} from '../render3/fields';
1313
import {compileComponent as render3CompileComponent, compileDirective as render3CompileDirective} from '../render3/jit/directive';
1414
import {compilePipe as render3CompilePipe} from '../render3/jit/pipe';
1515
import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators';
1616
import {noop} from '../util/noop';
1717
import {fillProperties} from '../util/property';
18+
1819
import {ViewEncapsulation} from './view';
1920

2021

@@ -714,54 +715,29 @@ const initializeBaseDef = (target: any): void => {
714715
};
715716

716717
/**
717-
* Returns a function that will update the static definition on a class to have the
718-
* appropriate input or output mapping.
719-
*
720-
* Will also add an {@link ngBaseDef} property to a directive if no `ngDirectiveDef`
721-
* or `ngComponentDef` is present. This is done because a class may have {@link InputDecorator}s and
722-
* {@link OutputDecorator}s without having a {@link ComponentDecorator} or {@link DirectiveDecorator},
723-
* and those inputs and outputs should still be inheritable, we need to add an
724-
* `ngBaseDef` property if there are no existing `ngComponentDef` or `ngDirectiveDef`
725-
* properties, so that we can track the inputs and outputs for inheritance purposes.
726-
*
727-
* @param getPropertyToUpdate A function that maps to either the `inputs` property or the
728-
* `outputs` property of a definition.
729-
* @returns A function that, the called, will add a `ngBaseDef` if no other definition is present,
730-
* then update the `inputs` or `outputs` on it, depending on what was selected by `getPropertyToUpdate`
731-
*
732-
*
733-
* @see InputDecorator
734-
* @see OutputDecorator
735-
* @see InheritenceFeature
718+
* Does the work of creating the `ngBaseDef` property for the @Input and @Output decorators.
719+
* @param key "inputs" or "outputs"
736720
*/
737-
function getOrCreateDefinitionAndUpdateMappingFor(
738-
getPropertyToUpdate: (baseDef: {inputs?: any, outputs?: any}) => any) {
739-
return function updateIOProp(target: any, name: string, ...args: any[]) {
740-
const constructor = target.constructor;
741-
742-
let def: any =
743-
constructor[NG_COMPONENT_DEF] || constructor[NG_DIRECTIVE_DEF] || constructor[NG_BASE_DEF];
744-
745-
if (!def) {
746-
initializeBaseDef(target);
747-
def = constructor[NG_BASE_DEF];
748-
}
749-
750-
const defProp = getPropertyToUpdate(def);
751-
// Use of `in` because we *do* want to check the prototype chain here.
752-
if (!(name in defProp)) {
721+
const updateBaseDefFromIOProp = (getProp: (baseDef: {inputs?: any, outputs?: any}) => any) =>
722+
(target: any, name: string, ...args: any[]) => {
723+
const constructor = target.constructor;
724+
725+
if (!constructor.hasOwnProperty(NG_BASE_DEF)) {
726+
initializeBaseDef(target);
727+
}
728+
729+
const baseDef = constructor.ngBaseDef;
730+
const defProp = getProp(baseDef);
753731
defProp[name] = args[0];
754-
}
755-
};
756-
}
732+
};
757733

758734
/**
759735
* @Annotation
760736
* @publicApi
761737
*/
762738
export const Input: InputDecorator = makePropDecorator(
763739
'Input', (bindingPropertyName?: string) => ({bindingPropertyName}), undefined,
764-
getOrCreateDefinitionAndUpdateMappingFor(def => def.inputs || {}));
740+
updateBaseDefFromIOProp(baseDef => baseDef.inputs || {}));
765741

766742
/**
767743
* Type of the Output decorator / constructor function.
@@ -801,7 +777,7 @@ export interface Output { bindingPropertyName?: string; }
801777
*/
802778
export const Output: OutputDecorator = makePropDecorator(
803779
'Output', (bindingPropertyName?: string) => ({bindingPropertyName}), undefined,
804-
getOrCreateDefinitionAndUpdateMappingFor(def => def.outputs || {}));
780+
updateBaseDefFromIOProp(baseDef => baseDef.outputs || {}));
805781

806782

807783

packages/core/src/render3/component.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {assertComponentType} from './assert';
1717
import {getComponentDef} from './definition';
1818
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
1919
import {publishDefaultGlobalUtils} from './global_utils';
20-
import {registerPostOrderHooks, registerPreOrderHooks} from './hooks';
2120
import {CLEAN_PROMISE, createLView, createNodeAtIndex, createTNode, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
2221
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
2322
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
@@ -26,6 +25,7 @@ import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './inte
2625
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
2726
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
2827
import {defaultScheduler, getRootView, readPatchedLView, renderStringify} from './util';
28+
import { registerPreOrderHooks, registerPostOrderHooks } from './hooks';
2929

3030

3131

@@ -240,8 +240,7 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
240240
registerPreOrderHooks(dirIndex, def, rootTView);
241241
// TODO(misko): replace `as TNode` with createTNode call. (needs refactoring to lose dep on
242242
// LNode).
243-
registerPostOrderHooks(
244-
rootTView, { directiveStart: dirIndex, directiveEnd: dirIndex + 1 } as TNode);
243+
registerPostOrderHooks(rootTView, { directiveStart: dirIndex, directiveEnd: dirIndex + 1 } as TNode);
245244
}
246245

247246
/**

packages/core/src/render3/context_discovery.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
1212
import {TNode, TNodeFlags} from './interfaces/node';
1313
import {RElement} from './interfaces/renderer';
1414
import {CONTEXT, HEADER_OFFSET, HOST, LView, TVIEW} from './interfaces/view';
15-
import {unwrapOnChangesDirectiveWrapper} from './onchanges_util';
1615
import {getComponentViewByIndex, getNativeByTNode, readElementValue, readPatchedData} from './util';
1716

1817

@@ -258,7 +257,7 @@ function findViaDirective(lView: LView, directiveInstance: {}): number {
258257
const directiveIndexStart = tNode.directiveStart;
259258
const directiveIndexEnd = tNode.directiveEnd;
260259
for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
261-
if (unwrapOnChangesDirectiveWrapper(lView[i]) === directiveInstance) {
260+
if (lView[i] === directiveInstance) {
262261
return tNode.index;
263262
}
264263
}

packages/core/src/render3/definition.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export function defineComponent<T>(componentDefinition: {
194194
/**
195195
* A list of optional features to apply.
196196
*
197-
* See: {@link ProvidersFeature}
197+
* See: {@link NgOnChangesFeature}, {@link ProvidersFeature}
198198
*/
199199
features?: ComponentDefFeature[];
200200

@@ -256,7 +256,6 @@ export function defineComponent<T>(componentDefinition: {
256256
inputs: null !, // assigned in noSideEffects
257257
outputs: null !, // assigned in noSideEffects
258258
exportAs: componentDefinition.exportAs || null,
259-
onChanges: typePrototype.ngOnChanges || null,
260259
onInit: typePrototype.ngOnInit || null,
261260
doCheck: typePrototype.ngDoCheck || null,
262261
afterContentInit: typePrototype.ngAfterContentInit || null,
@@ -567,7 +566,7 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
567566
/**
568567
* A list of optional features to apply.
569568
*
570-
* See: {@link ProvidersFeature}, {@link InheritDefinitionFeature}
569+
* See: {@link NgOnChangesFeature}, {@link ProvidersFeature}, {@link InheritDefinitionFeature}
571570
*/
572571
features?: DirectiveDefFeature[];
573572

packages/core/src/render3/di.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInject
2020
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node';
2121
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LView, TData, TVIEW, TView} from './interfaces/view';
2222
import {assertNodeOfPossibleTypes} from './node_assert';
23-
import {unwrapOnChangesDirectiveWrapper} from './onchanges_util';
2423
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
2524
import {findComponentView, getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, isComponentDef, renderStringify} from './util';
2625

@@ -523,8 +522,6 @@ export function getNodeInjectable(
523522
factory.resolving = false;
524523
setTNodeAndViewData(savePreviousOrParentTNode, saveLView);
525524
}
526-
} else {
527-
value = unwrapOnChangesDirectiveWrapper(value);
528525
}
529526
return value;
530527
}

0 commit comments

Comments
 (0)