refactor(ivy): Add newer, smaller NgOnChangesFeature (#28187)
PR Close #28187
This commit is contained in:
parent
5552661fd7
commit
a95e81978b
@ -34,14 +34,20 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChange
|
|||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngTemplateOutlet]'})
|
@Directive({selector: '[ngTemplateOutlet]'})
|
||||||
export class NgTemplateOutlet implements OnChanges {
|
export class NgTemplateOutlet implements OnChanges {
|
||||||
// TODO(issue/24571): remove '!'.
|
private _viewRef: EmbeddedViewRef<any>|null = null;
|
||||||
private _viewRef !: EmbeddedViewRef<any>;
|
|
||||||
|
|
||||||
// TODO(issue/24571): remove '!'.
|
/**
|
||||||
@Input() public ngTemplateOutletContext !: Object;
|
* A context object to attach to the {@link EmbeddedViewRef}. This should be an
|
||||||
|
* object, the object's keys will be available for binding by the local template `let`
|
||||||
|
* declarations.
|
||||||
|
* Using the key `$implicit` in the context object will set its value as default.
|
||||||
|
*/
|
||||||
|
@Input() public ngTemplateOutletContext: Object|null = null;
|
||||||
|
|
||||||
// TODO(issue/24571): remove '!'.
|
/**
|
||||||
@Input() public ngTemplateOutlet !: TemplateRef<any>;
|
* A string defining the template reference and optionally the context object for the template.
|
||||||
|
*/
|
||||||
|
@Input() public ngTemplateOutlet: TemplateRef<any>|null = null;
|
||||||
|
|
||||||
constructor(private _viewContainerRef: ViewContainerRef) {}
|
constructor(private _viewContainerRef: ViewContainerRef) {}
|
||||||
|
|
||||||
@ -97,7 +103,7 @@ export class NgTemplateOutlet implements OnChanges {
|
|||||||
|
|
||||||
private _updateExistingContext(ctx: Object): void {
|
private _updateExistingContext(ctx: Object): void {
|
||||||
for (let propName of Object.keys(ctx)) {
|
for (let propName of Object.keys(ctx)) {
|
||||||
(<any>this._viewRef.context)[propName] = (<any>this.ngTemplateOutletContext)[propName];
|
(<any>this._viewRef !.context)[propName] = (<any>this.ngTemplateOutletContext)[propName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,20 +64,6 @@ export class WrappedValue {
|
|||||||
static isWrapped(value: any): value is WrappedValue { return value instanceof WrappedValue; }
|
static isWrapped(value: any): value is WrappedValue { return value instanceof WrappedValue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a basic change from a previous to a new value.
|
|
||||||
*
|
|
||||||
* @publicApi
|
|
||||||
*/
|
|
||||||
export class SimpleChange {
|
|
||||||
constructor(public previousValue: any, public currentValue: any, public firstChange: boolean) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the new value is the first value assigned.
|
|
||||||
*/
|
|
||||||
isFirstChange(): boolean { return this.firstChange; }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isListLikeIterable(obj: any): boolean {
|
export function isListLikeIterable(obj: any): boolean {
|
||||||
if (!isJsObject(obj)) return false;
|
if (!isJsObject(obj)) return false;
|
||||||
return Array.isArray(obj) ||
|
return Array.isArray(obj) ||
|
||||||
|
@ -5,19 +5,9 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {SimpleChanges, SimpleChange} from './simple_change';
|
import {SimpleChanges} from './simple_change';
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines an object that associates properties with
|
|
||||||
* instances of `SimpleChange`.
|
|
||||||
*
|
|
||||||
* @see `OnChanges`
|
|
||||||
*
|
|
||||||
* @publicApi
|
|
||||||
*/
|
|
||||||
export interface SimpleChanges { [propName: string]: SimpleChange; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* A lifecycle hook that is called when any data-bound property of a directive changes.
|
* A lifecycle hook that is called when any data-bound property of a directive changes.
|
||||||
|
@ -17,6 +17,7 @@ import {assertComponentType} from './assert';
|
|||||||
import {getComponentDef} from './definition';
|
import {getComponentDef} from './definition';
|
||||||
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
||||||
import {publishDefaultGlobalUtils} from './global_utils';
|
import {publishDefaultGlobalUtils} from './global_utils';
|
||||||
|
import {registerPostOrderHooks, registerPreOrderHooks} from './hooks';
|
||||||
import {CLEAN_PROMISE, createLView, createNodeAtIndex, createTNode, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
|
import {CLEAN_PROMISE, createLView, createNodeAtIndex, createTNode, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions';
|
||||||
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
|
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
|
||||||
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||||
@ -25,7 +26,6 @@ import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './inte
|
|||||||
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||||
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
|
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
|
||||||
import {defaultScheduler, getRootView, readPatchedLView, renderStringify} from './util';
|
import {defaultScheduler, getRootView, readPatchedLView, renderStringify} from './util';
|
||||||
import { registerPreOrderHooks, registerPostOrderHooks } from './hooks';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -240,7 +240,8 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
|
|||||||
registerPreOrderHooks(dirIndex, def, rootTView);
|
registerPreOrderHooks(dirIndex, def, rootTView);
|
||||||
// TODO(misko): replace `as TNode` with createTNode call. (needs refactoring to lose dep on
|
// TODO(misko): replace `as TNode` with createTNode call. (needs refactoring to lose dep on
|
||||||
// LNode).
|
// LNode).
|
||||||
registerPostOrderHooks(rootTView, { directiveStart: dirIndex, directiveEnd: dirIndex + 1 } as TNode);
|
registerPostOrderHooks(
|
||||||
|
rootTView, { directiveStart: dirIndex, directiveEnd: dirIndex + 1 } as TNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -276,6 +276,7 @@ export function defineComponent<T>(componentDefinition: {
|
|||||||
id: 'c',
|
id: 'c',
|
||||||
styles: componentDefinition.styles || EMPTY_ARRAY,
|
styles: componentDefinition.styles || EMPTY_ARRAY,
|
||||||
_: null as never,
|
_: null as never,
|
||||||
|
setInput: null,
|
||||||
};
|
};
|
||||||
def._ = noSideEffects(() => {
|
def._ = noSideEffects(() => {
|
||||||
const directiveTypes = componentDefinition.directives !;
|
const directiveTypes = componentDefinition.directives !;
|
||||||
@ -380,12 +381,14 @@ export function defineNgModule<T>(def: {type: T} & Partial<NgModuleDef<T>>): nev
|
|||||||
*
|
*
|
||||||
|
|
||||||
*/
|
*/
|
||||||
function invertObject(obj: any, secondary?: any): any {
|
function invertObject<T>(
|
||||||
if (obj == null) return EMPTY_OBJ;
|
obj?: {[P in keyof T]?: string | [string, string]},
|
||||||
|
secondary?: {[key: string]: string}): {[P in keyof T]: string} {
|
||||||
|
if (obj == null) return EMPTY_OBJ as any;
|
||||||
const newLookup: any = {};
|
const newLookup: any = {};
|
||||||
for (const minifiedKey in obj) {
|
for (const minifiedKey in obj) {
|
||||||
if (obj.hasOwnProperty(minifiedKey)) {
|
if (obj.hasOwnProperty(minifiedKey)) {
|
||||||
let publicName: string = obj[minifiedKey];
|
let publicName: string|[string, string] = obj[minifiedKey] !;
|
||||||
let declaredName = publicName;
|
let declaredName = publicName;
|
||||||
if (Array.isArray(publicName)) {
|
if (Array.isArray(publicName)) {
|
||||||
declaredName = publicName[1];
|
declaredName = publicName[1];
|
||||||
@ -393,7 +396,7 @@ function invertObject(obj: any, secondary?: any): any {
|
|||||||
}
|
}
|
||||||
newLookup[publicName] = minifiedKey;
|
newLookup[publicName] = minifiedKey;
|
||||||
if (secondary) {
|
if (secondary) {
|
||||||
(secondary[publicName] = declaredName);
|
(secondary[publicName] = declaredName as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,11 +473,11 @@ export function defineBase<T>(baseDefinition: {
|
|||||||
*/
|
*/
|
||||||
outputs?: {[P in keyof T]?: string};
|
outputs?: {[P in keyof T]?: string};
|
||||||
}): BaseDef<T> {
|
}): BaseDef<T> {
|
||||||
const declaredInputs: {[P in keyof T]: P} = {} as any;
|
const declaredInputs: {[P in keyof T]: string} = {} as any;
|
||||||
return {
|
return {
|
||||||
inputs: invertObject(baseDefinition.inputs, declaredInputs),
|
inputs: invertObject<T>(baseDefinition.inputs as any, declaredInputs),
|
||||||
declaredInputs: declaredInputs,
|
declaredInputs: declaredInputs,
|
||||||
outputs: invertObject(baseDefinition.outputs),
|
outputs: invertObject<T>(baseDefinition.outputs as any),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Type} from '../../interface/type';
|
import {Type} from '../../interface/type';
|
||||||
|
import {Component} from '../../metadata/directives';
|
||||||
import {fillProperties} from '../../util/property';
|
import {fillProperties} from '../../util/property';
|
||||||
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
||||||
import {ComponentDef, DirectiveDef, DirectiveDefFeature, RenderFlags} from '../interfaces/definition';
|
import {ComponentDef, DirectiveDef, DirectiveDefFeature, RenderFlags} from '../interfaces/definition';
|
||||||
import { Component } from '../../metadata/directives';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {SimpleChange} from '../../change_detection/change_detection_util';
|
|
||||||
import {SimpleChanges} from '../../interface/simple_change';
|
|
||||||
import {OnChanges} from '../../interface/lifecycle_hooks';
|
import {OnChanges} from '../../interface/lifecycle_hooks';
|
||||||
|
import {SimpleChange, SimpleChanges} from '../../interface/simple_change';
|
||||||
|
import {EMPTY_OBJ} from '../empty';
|
||||||
import {DirectiveDef, DirectiveDefFeature} from '../interfaces/definition';
|
import {DirectiveDef, DirectiveDefFeature} from '../interfaces/definition';
|
||||||
|
|
||||||
const PRIVATE_PREFIX = '__ngOnChanges_';
|
const PRIVATE_PREFIX = '__ngOnChanges_';
|
||||||
@ -40,86 +40,63 @@ type OnChangesExpando = OnChanges & {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function NgOnChangesFeature<T>(definition: DirectiveDef<T>): void {
|
export function NgOnChangesFeature<T>(definition: DirectiveDef<T>): void {
|
||||||
const publicToDeclaredInputs = definition.declaredInputs;
|
if (definition.type.prototype.ngOnChanges) {
|
||||||
const publicToMinifiedInputs = definition.inputs;
|
definition.setInput = ngOnChangesSetInput;
|
||||||
const proto = definition.type.prototype;
|
|
||||||
for (const publicName in publicToDeclaredInputs) {
|
|
||||||
if (publicToDeclaredInputs.hasOwnProperty(publicName)) {
|
|
||||||
const minifiedKey = publicToMinifiedInputs[publicName];
|
|
||||||
const declaredKey = publicToDeclaredInputs[publicName];
|
|
||||||
const privateMinKey = PRIVATE_PREFIX + minifiedKey;
|
|
||||||
|
|
||||||
// Walk the prototype chain to see if we find a property descriptor
|
const prevDoCheck = definition.doCheck;
|
||||||
// That way we can honor setters and getters that were inherited.
|
const prevOnInit = definition.onInit;
|
||||||
let originalProperty: PropertyDescriptor|undefined = undefined;
|
|
||||||
let checkProto = proto;
|
|
||||||
while (!originalProperty && checkProto &&
|
|
||||||
Object.getPrototypeOf(checkProto) !== Object.getPrototypeOf(Object.prototype)) {
|
|
||||||
originalProperty = Object.getOwnPropertyDescriptor(checkProto, minifiedKey);
|
|
||||||
checkProto = Object.getPrototypeOf(checkProto);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getter = originalProperty && originalProperty.get;
|
definition.onInit = wrapOnChanges(prevOnInit);
|
||||||
const setter = originalProperty && originalProperty.set;
|
definition.doCheck = wrapOnChanges(prevDoCheck);
|
||||||
|
|
||||||
// create a getter and setter for property
|
|
||||||
Object.defineProperty(proto, minifiedKey, {
|
|
||||||
get: getter ||
|
|
||||||
(setter ? undefined : function(this: OnChangesExpando) { return this[privateMinKey]; }),
|
|
||||||
set<T>(this: OnChangesExpando, value: T) {
|
|
||||||
let simpleChanges = this[PRIVATE_PREFIX];
|
|
||||||
if (!simpleChanges) {
|
|
||||||
simpleChanges = {};
|
|
||||||
// Place where we will store SimpleChanges if there is a change
|
|
||||||
Object.defineProperty(this, PRIVATE_PREFIX, {value: simpleChanges, writable: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
const isFirstChange = !this.hasOwnProperty(privateMinKey);
|
|
||||||
const currentChange = simpleChanges[declaredKey];
|
|
||||||
|
|
||||||
if (currentChange) {
|
|
||||||
currentChange.currentValue = value;
|
|
||||||
} else {
|
|
||||||
simpleChanges[declaredKey] =
|
|
||||||
new SimpleChange(this[privateMinKey], value, isFirstChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFirstChange) {
|
|
||||||
// Create a place where the actual value will be stored and make it non-enumerable
|
|
||||||
Object.defineProperty(this, privateMinKey, {value, writable: true});
|
|
||||||
} else {
|
|
||||||
this[privateMinKey] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setter) setter.call(this, value);
|
|
||||||
},
|
|
||||||
// Make the property configurable in dev mode to allow overriding in tests
|
|
||||||
configurable: !!ngDevMode
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an onInit hook is defined, it will need to wrap the ngOnChanges call
|
function wrapOnChanges(hook: (() => void) | null) {
|
||||||
// so the call order is changes-init-check in creation mode. In subsequent
|
return function(this: OnChanges) {
|
||||||
// change detection runs, only the check wrapper will be called.
|
const simpleChangesStore = getSimpleChangesStore(this);
|
||||||
if (definition.onInit != null) {
|
const current = simpleChangesStore && simpleChangesStore.current;
|
||||||
definition.onInit = onChangesWrapper(definition.onInit);
|
|
||||||
|
if (current) {
|
||||||
|
simpleChangesStore !.previous = current;
|
||||||
|
simpleChangesStore !.current = null;
|
||||||
|
this.ngOnChanges(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
definition.doCheck = onChangesWrapper(definition.doCheck);
|
hook && hook.call(this);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ngOnChangesSetInput<T>(
|
||||||
|
this: DirectiveDef<T>, instance: T, value: any, publicName: string, privateName: string): void {
|
||||||
|
const simpleChangesStore = getSimpleChangesStore(instance) ||
|
||||||
|
setSimpleChangesStore(instance, {previous: EMPTY_OBJ, current: null});
|
||||||
|
const current = simpleChangesStore.current || (simpleChangesStore.current = {});
|
||||||
|
const previous = simpleChangesStore.previous;
|
||||||
|
|
||||||
|
const declaredName = (this.declaredInputs as{[key: string]: string})[publicName];
|
||||||
|
const previousChange = previous[declaredName];
|
||||||
|
current[declaredName] = new SimpleChange(
|
||||||
|
previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ);
|
||||||
|
|
||||||
|
(instance as any)[privateName] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__';
|
||||||
|
|
||||||
|
function getSimpleChangesStore(instance: any): null|NgSimpleChangesStore {
|
||||||
|
return instance[SIMPLE_CHANGES_STORE] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSimpleChangesStore(instance: any, store: NgSimpleChangesStore): NgSimpleChangesStore {
|
||||||
|
return instance[SIMPLE_CHANGES_STORE] = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This option ensures that the ngOnChanges lifecycle hook will be inherited
|
// This option ensures that the ngOnChanges lifecycle hook will be inherited
|
||||||
// from superclasses (in InheritDefinitionFeature).
|
// from superclasses (in InheritDefinitionFeature).
|
||||||
(NgOnChangesFeature as DirectiveDefFeature).ngInherit = true;
|
(NgOnChangesFeature as DirectiveDefFeature).ngInherit = true;
|
||||||
|
|
||||||
function onChangesWrapper(delegateHook: (() => void) | null) {
|
|
||||||
return function(this: OnChangesExpando) {
|
interface NgSimpleChangesStore {
|
||||||
const simpleChanges = this[PRIVATE_PREFIX];
|
previous: SimpleChanges;
|
||||||
if (simpleChanges != null) {
|
current: SimpleChanges|null;
|
||||||
this.ngOnChanges(simpleChanges);
|
|
||||||
this[PRIVATE_PREFIX] = null;
|
|
||||||
}
|
|
||||||
if (delegateHook) delegateHook.apply(this);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -959,10 +959,10 @@ function listenerInternal(
|
|||||||
const propsLength = props.length;
|
const propsLength = props.length;
|
||||||
if (propsLength) {
|
if (propsLength) {
|
||||||
const lCleanup = getCleanup(lView);
|
const lCleanup = getCleanup(lView);
|
||||||
for (let i = 0; i < propsLength; i += 2) {
|
for (let i = 0; i < propsLength; i += 3) {
|
||||||
const index = props[i] as number;
|
const index = props[i] as number;
|
||||||
ngDevMode && assertDataInRange(lView, index);
|
ngDevMode && assertDataInRange(lView, index);
|
||||||
const minifiedName = props[i + 1];
|
const minifiedName = props[i + 2];
|
||||||
const directiveInstance = lView[index];
|
const directiveInstance = lView[index];
|
||||||
const output = directiveInstance[minifiedName];
|
const output = directiveInstance[minifiedName];
|
||||||
|
|
||||||
@ -1214,18 +1214,29 @@ export function createTNode(
|
|||||||
* @param value Value to set.
|
* @param value Value to set.
|
||||||
*/
|
*/
|
||||||
function setInputsForProperty(lView: LView, inputs: PropertyAliasValue, value: any): void {
|
function setInputsForProperty(lView: LView, inputs: PropertyAliasValue, value: any): void {
|
||||||
for (let i = 0; i < inputs.length; i += 2) {
|
const tView = lView[TVIEW];
|
||||||
ngDevMode && assertDataInRange(lView, inputs[i] as number);
|
for (let i = 0; i < inputs.length;) {
|
||||||
lView[inputs[i] as number][inputs[i + 1]] = value;
|
const index = inputs[i++] as number;
|
||||||
|
const publicName = inputs[i++] as string;
|
||||||
|
const privateName = inputs[i++] as string;
|
||||||
|
const instance = lView[index];
|
||||||
|
ngDevMode && assertDataInRange(lView, index);
|
||||||
|
const def = tView.data[index] as DirectiveDef<any>;
|
||||||
|
const setInput = def.setInput;
|
||||||
|
if (setInput) {
|
||||||
|
def.setInput !(instance, value, publicName, privateName);
|
||||||
|
} else {
|
||||||
|
instance[privateName] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNgReflectProperties(
|
function setNgReflectProperties(
|
||||||
lView: LView, element: RElement | RComment, type: TNodeType, inputs: PropertyAliasValue,
|
lView: LView, element: RElement | RComment, type: TNodeType, inputs: PropertyAliasValue,
|
||||||
value: any) {
|
value: any) {
|
||||||
for (let i = 0; i < inputs.length; i += 2) {
|
for (let i = 0; i < inputs.length; i += 3) {
|
||||||
const renderer = lView[RENDERER];
|
const renderer = lView[RENDERER];
|
||||||
const attrName = normalizeDebugBindingName(inputs[i + 1] as string);
|
const attrName = normalizeDebugBindingName(inputs[i + 2] as string);
|
||||||
const debugValue = normalizeDebugBindingValue(value);
|
const debugValue = normalizeDebugBindingValue(value);
|
||||||
if (type === TNodeType.Element) {
|
if (type === TNodeType.Element) {
|
||||||
isProceduralRenderer(renderer) ?
|
isProceduralRenderer(renderer) ?
|
||||||
@ -1268,8 +1279,8 @@ function generatePropertyAliases(tNode: TNode, direction: BindingDirection): Pro
|
|||||||
propStore = propStore || {};
|
propStore = propStore || {};
|
||||||
const internalName = propertyAliasMap[publicName];
|
const internalName = propertyAliasMap[publicName];
|
||||||
const hasProperty = propStore.hasOwnProperty(publicName);
|
const hasProperty = propStore.hasOwnProperty(publicName);
|
||||||
hasProperty ? propStore[publicName].push(i, internalName) :
|
hasProperty ? propStore[publicName].push(i, publicName, internalName) :
|
||||||
(propStore[publicName] = [i, internalName]);
|
(propStore[publicName] = [i, publicName, internalName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1702,7 +1713,7 @@ function postProcessDirective<T>(
|
|||||||
postProcessBaseDirective(viewData, previousOrParentTNode, directive, def);
|
postProcessBaseDirective(viewData, previousOrParentTNode, directive, def);
|
||||||
ngDevMode && assertDefined(previousOrParentTNode, 'previousOrParentTNode');
|
ngDevMode && assertDefined(previousOrParentTNode, 'previousOrParentTNode');
|
||||||
if (previousOrParentTNode && previousOrParentTNode.attrs) {
|
if (previousOrParentTNode && previousOrParentTNode.attrs) {
|
||||||
setInputsFromAttrs(directiveDefIdx, directive, def.inputs, previousOrParentTNode);
|
setInputsFromAttrs(directiveDefIdx, directive, def, previousOrParentTNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def.contentQueries) {
|
if (def.contentQueries) {
|
||||||
@ -1903,16 +1914,24 @@ function addComponentLogic<T>(
|
|||||||
* @param tNode The static data for this node
|
* @param tNode The static data for this node
|
||||||
*/
|
*/
|
||||||
function setInputsFromAttrs<T>(
|
function setInputsFromAttrs<T>(
|
||||||
directiveIndex: number, instance: T, inputs: {[P in keyof T]: string;}, tNode: TNode): void {
|
directiveIndex: number, instance: T, def: DirectiveDef<T>, tNode: TNode): void {
|
||||||
let initialInputData = tNode.initialInputs as InitialInputData | undefined;
|
let initialInputData = tNode.initialInputs as InitialInputData | undefined;
|
||||||
if (initialInputData === undefined || directiveIndex >= initialInputData.length) {
|
if (initialInputData === undefined || directiveIndex >= initialInputData.length) {
|
||||||
initialInputData = generateInitialInputs(directiveIndex, inputs, tNode);
|
initialInputData = generateInitialInputs(directiveIndex, def.inputs, tNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialInputs: InitialInputs|null = initialInputData[directiveIndex];
|
const initialInputs: InitialInputs|null = initialInputData[directiveIndex];
|
||||||
if (initialInputs) {
|
if (initialInputs) {
|
||||||
for (let i = 0; i < initialInputs.length; i += 2) {
|
const setInput = def.setInput;
|
||||||
(instance as any)[initialInputs[i]] = initialInputs[i + 1];
|
for (let i = 0; i < initialInputs.length;) {
|
||||||
|
const publicName = initialInputs[i++];
|
||||||
|
const privateName = initialInputs[i++];
|
||||||
|
const value = initialInputs[i++];
|
||||||
|
if (setInput) {
|
||||||
|
def.setInput !(instance, value, publicName, privateName);
|
||||||
|
} else {
|
||||||
|
(instance as any)[privateName] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1956,7 +1975,7 @@ function generateInitialInputs(
|
|||||||
if (minifiedInputName !== undefined) {
|
if (minifiedInputName !== undefined) {
|
||||||
const inputsToStore: InitialInputs =
|
const inputsToStore: InitialInputs =
|
||||||
initialInputData[directiveIndex] || (initialInputData[directiveIndex] = []);
|
initialInputData[directiveIndex] || (initialInputData[directiveIndex] = []);
|
||||||
inputsToStore.push(minifiedInputName, attrValue as string);
|
inputsToStore.push(attrName, minifiedInputName, attrValue as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 2;
|
i += 2;
|
||||||
|
@ -84,14 +84,14 @@ export interface BaseDef<T> {
|
|||||||
* @deprecated This is only here because `NgOnChanges` incorrectly uses declared name instead of
|
* @deprecated This is only here because `NgOnChanges` incorrectly uses declared name instead of
|
||||||
* public or minified name.
|
* public or minified name.
|
||||||
*/
|
*/
|
||||||
readonly declaredInputs: {[P in keyof T]: P};
|
readonly declaredInputs: {[P in keyof T]: string};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dictionary mapping the outputs' minified property names to their public API names, which
|
* A dictionary mapping the outputs' minified property names to their public API names, which
|
||||||
* are their aliases if any, or their original unminified property names
|
* are their aliases if any, or their original unminified property names
|
||||||
* (as in `@Output('alias') propertyName: any;`).
|
* (as in `@Output('alias') propertyName: any;`).
|
||||||
*/
|
*/
|
||||||
readonly outputs: {[P in keyof T]: P};
|
readonly outputs: {[P in keyof T]: string};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,6 +152,10 @@ export interface DirectiveDef<T> extends BaseDef<T> {
|
|||||||
* The features applied to this directive
|
* The features applied to this directive
|
||||||
*/
|
*/
|
||||||
readonly features: DirectiveDefFeature[]|null;
|
readonly features: DirectiveDefFeature[]|null;
|
||||||
|
|
||||||
|
setInput:
|
||||||
|
((this: DirectiveDef<T>, instance: T, value: any, publicName: string,
|
||||||
|
privateName: string) => void)|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComponentDefWithMeta<
|
export type ComponentDefWithMeta<
|
||||||
|
@ -468,10 +468,11 @@ export type PropertyAliases = {
|
|||||||
/**
|
/**
|
||||||
* Store the runtime input or output names for all the directives.
|
* Store the runtime input or output names for all the directives.
|
||||||
*
|
*
|
||||||
* - Even indices: directive index
|
* i+0: directive instance index
|
||||||
* - Odd indices: minified / internal name
|
* i+1: publicName
|
||||||
|
* i+2: privateName
|
||||||
*
|
*
|
||||||
* e.g. [0, 'change-minified']
|
* e.g. [0, 'change', 'change-minified']
|
||||||
*/
|
*/
|
||||||
export type PropertyAliasValue = (number | string)[];
|
export type PropertyAliasValue = (number | string)[];
|
||||||
|
|
||||||
@ -484,14 +485,15 @@ export type PropertyAliasValue = (number | string)[];
|
|||||||
*
|
*
|
||||||
* Within each sub-array:
|
* Within each sub-array:
|
||||||
*
|
*
|
||||||
* Even indices: minified/internal input name
|
* i+0: attribute name
|
||||||
* Odd indices: initial value
|
* i+1: minified/internal input name
|
||||||
|
* i+2: initial value
|
||||||
*
|
*
|
||||||
* If a directive on a node does not have any input properties
|
* If a directive on a node does not have any input properties
|
||||||
* that should be set from attributes, its index is set to null
|
* that should be set from attributes, its index is set to null
|
||||||
* to avoid a sparse array.
|
* to avoid a sparse array.
|
||||||
*
|
*
|
||||||
* e.g. [null, ['role-min', 'button']]
|
* e.g. [null, ['role-min', 'minified-input', 'button']]
|
||||||
*/
|
*/
|
||||||
export type InitialInputData = (InitialInputs | null)[];
|
export type InitialInputData = (InitialInputs | null)[];
|
||||||
|
|
||||||
@ -499,10 +501,11 @@ export type InitialInputData = (InitialInputs | null)[];
|
|||||||
* Used by InitialInputData to store input properties
|
* Used by InitialInputData to store input properties
|
||||||
* that should be set once from attributes.
|
* that should be set once from attributes.
|
||||||
*
|
*
|
||||||
* Even indices: minified/internal input name
|
* i+0: attribute name
|
||||||
* Odd indices: initial value
|
* i+1: minified/internal input name
|
||||||
|
* i+2: initial value
|
||||||
*
|
*
|
||||||
* e.g. ['role-min', 'button']
|
* e.g. ['role-min', 'minified-input', 'button']
|
||||||
*/
|
*/
|
||||||
export type InitialInputs = string[];
|
export type InitialInputs = string[];
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import {InjectionToken} from '../../di/injection_token';
|
import {InjectionToken} from '../../di/injection_token';
|
||||||
import {Injector} from '../../di/injector';
|
import {Injector} from '../../di/injector';
|
||||||
import {SimpleChanges} from '../../interface/simple_change';
|
|
||||||
import {Type} from '../../interface/type';
|
import {Type} from '../../interface/type';
|
||||||
import {QueryList} from '../../linker';
|
import {QueryList} from '../../linker';
|
||||||
import {Sanitizer} from '../../sanitization/security';
|
import {Sanitizer} from '../../sanitization/security';
|
||||||
|
@ -95,9 +95,6 @@
|
|||||||
{
|
{
|
||||||
"name": "PARENT_INJECTOR"
|
"name": "PARENT_INJECTOR"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "PRIVATE_PREFIX"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "RENDERER"
|
"name": "RENDERER"
|
||||||
},
|
},
|
||||||
@ -107,6 +104,9 @@
|
|||||||
{
|
{
|
||||||
"name": "SANITIZER"
|
"name": "SANITIZER"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "SIMPLE_CHANGES_STORE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "SimpleChange"
|
"name": "SimpleChange"
|
||||||
},
|
},
|
||||||
@ -308,6 +308,9 @@
|
|||||||
{
|
{
|
||||||
"name": "getRootView"
|
"name": "getRootView"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getSimpleChangesStore"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasParentInjector"
|
"name": "hasParentInjector"
|
||||||
},
|
},
|
||||||
@ -366,10 +369,10 @@
|
|||||||
"name": "nextNgElementId"
|
"name": "nextNgElementId"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "noSideEffects"
|
"name": "ngOnChangesSetInput"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "onChangesWrapper"
|
"name": "noSideEffects"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "postProcessBaseDirective"
|
"name": "postProcessBaseDirective"
|
||||||
@ -437,6 +440,9 @@
|
|||||||
{
|
{
|
||||||
"name": "setPreviousOrParentTNode"
|
"name": "setPreviousOrParentTNode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setSimpleChangesStore"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setTNodeAndViewData"
|
"name": "setTNodeAndViewData"
|
||||||
},
|
},
|
||||||
@ -454,5 +460,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "viewAttached"
|
"name": "viewAttached"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wrapOnChanges"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -8,6 +8,9 @@
|
|||||||
{
|
{
|
||||||
"name": "EMPTY_ARRAY"
|
"name": "EMPTY_ARRAY"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "EMPTY_OBJ"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "EmptyErrorImpl"
|
"name": "EmptyErrorImpl"
|
||||||
},
|
},
|
||||||
@ -51,10 +54,10 @@
|
|||||||
"name": "PARAMETERS"
|
"name": "PARAMETERS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "PRIVATE_PREFIX"
|
"name": "R3Injector"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "R3Injector"
|
"name": "SIMPLE_CHANGES_STORE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ScopedService"
|
"name": "ScopedService"
|
||||||
@ -122,6 +125,9 @@
|
|||||||
{
|
{
|
||||||
"name": "getNullInjector"
|
"name": "getNullInjector"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getSimpleChangesStore"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasDeps"
|
"name": "hasDeps"
|
||||||
},
|
},
|
||||||
@ -165,7 +171,7 @@
|
|||||||
"name": "makeRecord"
|
"name": "makeRecord"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "onChangesWrapper"
|
"name": "ngOnChangesSetInput"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "providerToFactory"
|
"name": "providerToFactory"
|
||||||
@ -179,7 +185,13 @@
|
|||||||
{
|
{
|
||||||
"name": "setCurrentInjector"
|
"name": "setCurrentInjector"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setSimpleChangesStore"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "stringify"
|
"name": "stringify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wrapOnChanges"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -167,9 +167,6 @@
|
|||||||
{
|
{
|
||||||
"name": "PARENT_INJECTOR"
|
"name": "PARENT_INJECTOR"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "PRIVATE_PREFIX"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "QUERIES"
|
"name": "QUERIES"
|
||||||
},
|
},
|
||||||
@ -188,6 +185,9 @@
|
|||||||
{
|
{
|
||||||
"name": "SANITIZER"
|
"name": "SANITIZER"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "SIMPLE_CHANGES_STORE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "SWITCH_ELEMENT_REF_FACTORY"
|
"name": "SWITCH_ELEMENT_REF_FACTORY"
|
||||||
},
|
},
|
||||||
@ -785,6 +785,9 @@
|
|||||||
{
|
{
|
||||||
"name": "getRootView"
|
"name": "getRootView"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getSimpleChangesStore"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getSinglePropIndexValue"
|
"name": "getSinglePropIndexValue"
|
||||||
},
|
},
|
||||||
@ -1014,10 +1017,10 @@
|
|||||||
"name": "nextNgElementId"
|
"name": "nextNgElementId"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "noSideEffects"
|
"name": "ngOnChangesSetInput"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "onChangesWrapper"
|
"name": "noSideEffects"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pointers"
|
"name": "pointers"
|
||||||
@ -1184,6 +1187,9 @@
|
|||||||
{
|
{
|
||||||
"name": "setSanitizeFlag"
|
"name": "setSanitizeFlag"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setSimpleChangesStore"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setStyle"
|
"name": "setStyle"
|
||||||
},
|
},
|
||||||
@ -1249,5 +1255,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "wrapListenerWithPreventDefault"
|
"name": "wrapListenerWithPreventDefault"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wrapOnChanges"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -536,7 +536,6 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||||||
expect(renderLog.log).toEqual(['someProp=Megatron']);
|
expect(renderLog.log).toEqual(['someProp=Megatron']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-956: refactor onChanges').
|
|
||||||
it('should record unwrapped values via ngOnChanges', fakeAsync(() => {
|
it('should record unwrapped values via ngOnChanges', fakeAsync(() => {
|
||||||
const ctx = createCompFixture(
|
const ctx = createCompFixture(
|
||||||
'<div [testDirective]="\'aName\' | wrappedPipe" [a]="1" [b]="2 | wrappedPipe"></div>');
|
'<div [testDirective]="\'aName\' | wrappedPipe" [a]="1" [b]="2 | wrappedPipe"></div>');
|
||||||
@ -739,7 +738,6 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('ngOnChanges', () => {
|
describe('ngOnChanges', () => {
|
||||||
fixmeIvy('FW-956: refactor onChanges').
|
|
||||||
it('should notify the directive when a group of records changes', fakeAsync(() => {
|
it('should notify the directive when a group of records changes', fakeAsync(() => {
|
||||||
const ctx = createCompFixture(
|
const ctx = createCompFixture(
|
||||||
'<div [testDirective]="\'aName\'" [a]="1" [b]="2"></div><div [testDirective]="\'bName\'" [a]="4"></div>');
|
'<div [testDirective]="\'aName\'" [a]="1" [b]="2"></div><div [testDirective]="\'bName\'" [a]="4"></div>');
|
||||||
|
@ -519,7 +519,10 @@ describe('InheritDefinitionFeature', () => {
|
|||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
element(0, 'div', ['subDir', '']);
|
element(0, 'div', ['subDir', '']);
|
||||||
}
|
}
|
||||||
}, 1, 0, [SubDirective]);
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementProperty(0, 'someInput', bind(1));
|
||||||
|
}
|
||||||
|
}, 1, 1, [SubDirective]);
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
const fixture = new ComponentFixture(App);
|
||||||
expect(log).toEqual(['on changes!']);
|
expect(log).toEqual(['on changes!']);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {ComponentFactoryResolver, OnDestroy, SimpleChange, SimpleChanges, ViewContainerRef} from '../../src/core';
|
import {ComponentFactoryResolver, OnDestroy, SimpleChange, SimpleChanges, ViewContainerRef} from '../../src/core';
|
||||||
import {AttributeMarker, ComponentTemplate, LifecycleHooksFeature, NO_CHANGE, defineComponent, defineDirective, injectComponentFactoryResolver} from '../../src/render3/index';
|
import {AttributeMarker, ComponentTemplate, LifecycleHooksFeature, NO_CHANGE, NgOnChangesFeature, defineComponent, defineDirective, injectComponentFactoryResolver} from '../../src/render3/index';
|
||||||
|
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, listener, markDirty, projection, projectionDef, store, template, text} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, listener, markDirty, projection, projectionDef, store, template, text} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
@ -1941,7 +1941,6 @@ describe('lifecycles', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('FW-956: refactor onChanges').
|
|
||||||
describe('onChanges', () => {
|
describe('onChanges', () => {
|
||||||
let events: ({type: string, name: string, [key: string]: any})[];
|
let events: ({type: string, name: string, [key: string]: any})[];
|
||||||
|
|
||||||
@ -2008,7 +2007,8 @@ describe('lifecycles', () => {
|
|||||||
consts: consts,
|
consts: consts,
|
||||||
vars: vars,
|
vars: vars,
|
||||||
inputs: {a: 'val1', b: ['publicVal2', 'val2']}, template,
|
inputs: {a: 'val1', b: ['publicVal2', 'val2']}, template,
|
||||||
directives: directives
|
directives: directives,
|
||||||
|
features: [NgOnChangesFeature],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2026,7 +2026,8 @@ describe('lifecycles', () => {
|
|||||||
type: Directive,
|
type: Directive,
|
||||||
selectors: [['', 'dir', '']],
|
selectors: [['', 'dir', '']],
|
||||||
factory: () => new Directive(),
|
factory: () => new Directive(),
|
||||||
inputs: {a: 'val1', b: ['publicVal2', 'val2']}
|
inputs: {a: 'val1', b: ['publicVal2', 'val2']},
|
||||||
|
features: [NgOnChangesFeature],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2701,7 +2702,6 @@ describe('lifecycles', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('FW-956: refactor onChanges').
|
|
||||||
describe('hook order', () => {
|
describe('hook order', () => {
|
||||||
let events: string[];
|
let events: string[];
|
||||||
|
|
||||||
@ -2731,7 +2731,8 @@ describe('lifecycles', () => {
|
|||||||
consts: consts,
|
consts: consts,
|
||||||
vars: vars,
|
vars: vars,
|
||||||
inputs: {val: 'val'}, template,
|
inputs: {val: 'val'}, template,
|
||||||
directives: directives
|
directives: directives,
|
||||||
|
features: [NgOnChangesFeature],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,327 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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 {DoCheck, OnChanges, SimpleChange, SimpleChanges} from '../../src/core';
|
|
||||||
import {InheritDefinitionFeature} from '../../src/render3/features/inherit_definition_feature';
|
|
||||||
import {DirectiveDef, NgOnChangesFeature, defineDirective} from '../../src/render3/index';
|
|
||||||
import { fixmeIvy } from '@angular/private/testing';
|
|
||||||
|
|
||||||
fixmeIvy('FW-956: refactor onChanges').
|
|
||||||
describe('NgOnChangesFeature', () => {
|
|
||||||
it('should patch class', () => {
|
|
||||||
class MyDirective implements OnChanges, DoCheck {
|
|
||||||
public log: Array<string|SimpleChange> = [];
|
|
||||||
public valA: string = 'initValue';
|
|
||||||
public set valB(value: string) { this.log.push(value); }
|
|
||||||
|
|
||||||
public get valB() { return 'works'; }
|
|
||||||
|
|
||||||
ngDoCheck(): void { this.log.push('ngDoCheck'); }
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
this.log.push('ngOnChanges');
|
|
||||||
this.log.push('valA', changes['valA']);
|
|
||||||
this.log.push('valB', changes['valB']);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: MyDirective,
|
|
||||||
selectors: [['', 'myDir', '']],
|
|
||||||
factory: () => new MyDirective(),
|
|
||||||
features: [NgOnChangesFeature],
|
|
||||||
inputs: {valA: 'valA', valB: 'valB'}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const myDir =
|
|
||||||
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory(null) as MyDirective;
|
|
||||||
myDir.valA = 'first';
|
|
||||||
expect(myDir.valA).toEqual('first');
|
|
||||||
myDir.valB = 'second';
|
|
||||||
expect(myDir.log).toEqual(['second']);
|
|
||||||
expect(myDir.valB).toEqual('works');
|
|
||||||
myDir.log.length = 0;
|
|
||||||
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).doCheck !.call(myDir);
|
|
||||||
const changeA = new SimpleChange(undefined, 'first', true);
|
|
||||||
const changeB = new SimpleChange(undefined, 'second', true);
|
|
||||||
expect(myDir.log).toEqual(['ngOnChanges', 'valA', changeA, 'valB', changeB, 'ngDoCheck']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inherit the behavior from super class', () => {
|
|
||||||
const log: any[] = [];
|
|
||||||
|
|
||||||
class SuperDirective implements OnChanges, DoCheck {
|
|
||||||
valA = 'initValue';
|
|
||||||
|
|
||||||
set valB(value: string) { log.push(value); }
|
|
||||||
|
|
||||||
get valB() { return 'works'; }
|
|
||||||
|
|
||||||
ngDoCheck(): void { log.push('ngDoCheck'); }
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
log.push('ngOnChanges');
|
|
||||||
log.push('valA', changes['valA']);
|
|
||||||
log.push('valB', changes['valB']);
|
|
||||||
log.push('valC', changes['valC']);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: SuperDirective,
|
|
||||||
selectors: [['', 'superDir', '']],
|
|
||||||
factory: () => new SuperDirective(),
|
|
||||||
features: [NgOnChangesFeature],
|
|
||||||
inputs: {valA: 'valA', valB: 'valB'},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubDirective extends SuperDirective {
|
|
||||||
valC = 'initValue';
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: SubDirective,
|
|
||||||
selectors: [['', 'subDir', '']],
|
|
||||||
factory: () => new SubDirective(),
|
|
||||||
features: [InheritDefinitionFeature],
|
|
||||||
inputs: {valC: 'valC'},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const myDir =
|
|
||||||
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory(null) as SubDirective;
|
|
||||||
myDir.valA = 'first';
|
|
||||||
expect(myDir.valA).toEqual('first');
|
|
||||||
|
|
||||||
myDir.valB = 'second';
|
|
||||||
expect(myDir.valB).toEqual('works');
|
|
||||||
|
|
||||||
myDir.valC = 'third';
|
|
||||||
expect(myDir.valC).toEqual('third');
|
|
||||||
|
|
||||||
log.length = 0;
|
|
||||||
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).doCheck !.call(myDir);
|
|
||||||
const changeA = new SimpleChange(undefined, 'first', true);
|
|
||||||
const changeB = new SimpleChange(undefined, 'second', true);
|
|
||||||
const changeC = new SimpleChange(undefined, 'third', true);
|
|
||||||
|
|
||||||
expect(log).toEqual(
|
|
||||||
['ngOnChanges', 'valA', changeA, 'valB', changeB, 'valC', changeC, 'ngDoCheck']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not run the parent doCheck if it is not called explicitly on super class', () => {
|
|
||||||
const log: any[] = [];
|
|
||||||
|
|
||||||
class SuperDirective implements OnChanges, DoCheck {
|
|
||||||
valA = 'initValue';
|
|
||||||
|
|
||||||
ngDoCheck(): void { log.push('ERROR: Child overrides it without super call'); }
|
|
||||||
ngOnChanges(changes: SimpleChanges): void { log.push(changes.valA, changes.valB); }
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: SuperDirective,
|
|
||||||
selectors: [['', 'superDir', '']],
|
|
||||||
factory: () => new SuperDirective(),
|
|
||||||
features: [NgOnChangesFeature],
|
|
||||||
inputs: {valA: 'valA'},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubDirective extends SuperDirective implements DoCheck {
|
|
||||||
valB = 'initValue';
|
|
||||||
|
|
||||||
ngDoCheck(): void { log.push('sub ngDoCheck'); }
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: SubDirective,
|
|
||||||
selectors: [['', 'subDir', '']],
|
|
||||||
factory: () => new SubDirective(),
|
|
||||||
features: [InheritDefinitionFeature],
|
|
||||||
inputs: {valB: 'valB'},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const myDir =
|
|
||||||
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory(null) as SubDirective;
|
|
||||||
myDir.valA = 'first';
|
|
||||||
myDir.valB = 'second';
|
|
||||||
|
|
||||||
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).doCheck !.call(myDir);
|
|
||||||
const changeA = new SimpleChange(undefined, 'first', true);
|
|
||||||
const changeB = new SimpleChange(undefined, 'second', true);
|
|
||||||
expect(log).toEqual([changeA, changeB, 'sub ngDoCheck']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should run the parent doCheck if it is inherited from super class', () => {
|
|
||||||
const log: any[] = [];
|
|
||||||
|
|
||||||
class SuperDirective implements OnChanges, DoCheck {
|
|
||||||
valA = 'initValue';
|
|
||||||
|
|
||||||
ngDoCheck(): void { log.push('super ngDoCheck'); }
|
|
||||||
ngOnChanges(changes: SimpleChanges): void { log.push(changes.valA, changes.valB); }
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: SuperDirective,
|
|
||||||
selectors: [['', 'superDir', '']],
|
|
||||||
factory: () => new SuperDirective(),
|
|
||||||
features: [NgOnChangesFeature],
|
|
||||||
inputs: {valA: 'valA'},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubDirective extends SuperDirective implements DoCheck {
|
|
||||||
valB = 'initValue';
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: SubDirective,
|
|
||||||
selectors: [['', 'subDir', '']],
|
|
||||||
factory: () => new SubDirective(),
|
|
||||||
features: [InheritDefinitionFeature],
|
|
||||||
inputs: {valB: 'valB'},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const myDir =
|
|
||||||
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory(null) as SubDirective;
|
|
||||||
myDir.valA = 'first';
|
|
||||||
myDir.valB = 'second';
|
|
||||||
|
|
||||||
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).doCheck !.call(myDir);
|
|
||||||
const changeA = new SimpleChange(undefined, 'first', true);
|
|
||||||
const changeB = new SimpleChange(undefined, 'second', true);
|
|
||||||
expect(log).toEqual([changeA, changeB, 'super ngDoCheck']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should apply the feature to inherited properties if on sub class', () => {
|
|
||||||
const log: any[] = [];
|
|
||||||
|
|
||||||
class SuperDirective {
|
|
||||||
valC = 'initValue';
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: SuperDirective,
|
|
||||||
selectors: [['', 'subDir', '']],
|
|
||||||
factory: () => new SuperDirective(),
|
|
||||||
features: [],
|
|
||||||
inputs: {valC: 'valC'},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubDirective extends SuperDirective implements OnChanges, DoCheck {
|
|
||||||
valA = 'initValue';
|
|
||||||
|
|
||||||
set valB(value: string) { log.push(value); }
|
|
||||||
|
|
||||||
get valB() { return 'works'; }
|
|
||||||
|
|
||||||
ngDoCheck(): void { log.push('ngDoCheck'); }
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
log.push('ngOnChanges');
|
|
||||||
log.push('valA', changes['valA']);
|
|
||||||
log.push('valB', changes['valB']);
|
|
||||||
log.push('valC', changes['valC']);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: SubDirective,
|
|
||||||
selectors: [['', 'superDir', '']],
|
|
||||||
factory: () => new SubDirective(),
|
|
||||||
// Inheritance must always be before OnChanges feature.
|
|
||||||
features: [
|
|
||||||
InheritDefinitionFeature,
|
|
||||||
NgOnChangesFeature,
|
|
||||||
],
|
|
||||||
inputs: {valA: 'valA', valB: 'valB'}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const myDir =
|
|
||||||
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory(null) as SubDirective;
|
|
||||||
myDir.valA = 'first';
|
|
||||||
expect(myDir.valA).toEqual('first');
|
|
||||||
|
|
||||||
myDir.valB = 'second';
|
|
||||||
expect(log).toEqual(['second']);
|
|
||||||
expect(myDir.valB).toEqual('works');
|
|
||||||
|
|
||||||
myDir.valC = 'third';
|
|
||||||
expect(myDir.valC).toEqual('third');
|
|
||||||
|
|
||||||
log.length = 0;
|
|
||||||
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).doCheck !.call(myDir);
|
|
||||||
const changeA = new SimpleChange(undefined, 'first', true);
|
|
||||||
const changeB = new SimpleChange(undefined, 'second', true);
|
|
||||||
const changeC = new SimpleChange(undefined, 'third', true);
|
|
||||||
expect(log).toEqual(
|
|
||||||
['ngOnChanges', 'valA', changeA, 'valB', changeB, 'valC', changeC, 'ngDoCheck']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('correctly computes firstChange', () => {
|
|
||||||
class MyDirective implements OnChanges {
|
|
||||||
public log: Array<string|SimpleChange|undefined> = [];
|
|
||||||
public valA: string = 'initValue';
|
|
||||||
// TODO(issue/24571): remove '!'.
|
|
||||||
public valB !: string;
|
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
this.log.push('valA', changes['valA']);
|
|
||||||
this.log.push('valB', changes['valB']);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: MyDirective,
|
|
||||||
selectors: [['', 'myDir', '']],
|
|
||||||
factory: () => new MyDirective(),
|
|
||||||
features: [NgOnChangesFeature],
|
|
||||||
inputs: {valA: 'valA', valB: 'valB'}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const myDir =
|
|
||||||
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory(null) as MyDirective;
|
|
||||||
myDir.valA = 'first';
|
|
||||||
myDir.valB = 'second';
|
|
||||||
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).doCheck !.call(myDir);
|
|
||||||
const changeA1 = new SimpleChange(undefined, 'first', true);
|
|
||||||
const changeB1 = new SimpleChange(undefined, 'second', true);
|
|
||||||
expect(myDir.log).toEqual(['valA', changeA1, 'valB', changeB1]);
|
|
||||||
|
|
||||||
myDir.log.length = 0;
|
|
||||||
myDir.valA = 'third';
|
|
||||||
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).doCheck !.call(myDir);
|
|
||||||
const changeA2 = new SimpleChange('first', 'third', false);
|
|
||||||
expect(myDir.log).toEqual(['valA', changeA2, 'valB', undefined]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not create a getter when only a setter is originally defined', () => {
|
|
||||||
class MyDirective implements OnChanges {
|
|
||||||
public log: Array<string|SimpleChange> = [];
|
|
||||||
|
|
||||||
public set onlySetter(value: string) { this.log.push(value); }
|
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
this.log.push('ngOnChanges');
|
|
||||||
this.log.push('onlySetter', changes['onlySetter']);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ngDirectiveDef = defineDirective({
|
|
||||||
type: MyDirective,
|
|
||||||
selectors: [['', 'myDir', '']],
|
|
||||||
factory: () => new MyDirective(),
|
|
||||||
features: [NgOnChangesFeature],
|
|
||||||
inputs: {onlySetter: 'onlySetter'}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const myDir =
|
|
||||||
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory(null) as MyDirective;
|
|
||||||
myDir.onlySetter = 'someValue';
|
|
||||||
expect(myDir.onlySetter).toBeUndefined();
|
|
||||||
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).doCheck !.call(myDir);
|
|
||||||
const changeSetter = new SimpleChange(undefined, 'someValue', true);
|
|
||||||
expect(myDir.log).toEqual(['someValue', 'ngOnChanges', 'onlySetter', changeSetter]);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1677,7 +1677,8 @@ describe('ViewContainerRef', () => {
|
|||||||
elementProperty(3, 'name', bind('B'));
|
elementProperty(3, 'name', bind('B'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
directives: [ComponentWithHooks, DirectiveWithVCRef]
|
directives: [ComponentWithHooks, DirectiveWithVCRef],
|
||||||
|
features: [NgOnChangesFeature],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1769,7 +1770,8 @@ describe('ViewContainerRef', () => {
|
|||||||
elementProperty(1, 'name', bind('B'));
|
elementProperty(1, 'name', bind('B'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
directives: [ComponentWithHooks, DirectiveWithVCRef]
|
directives: [ComponentWithHooks, DirectiveWithVCRef],
|
||||||
|
features: [NgOnChangesFeature],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1801,7 +1803,7 @@ describe('ViewContainerRef', () => {
|
|||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html).toEqual('<hooks vcref="">A</hooks><hooks>D</hooks><hooks>B</hooks>');
|
expect(fixture.html).toEqual('<hooks vcref="">A</hooks><hooks>D</hooks><hooks>B</hooks>');
|
||||||
expect(log).toEqual([
|
expect(log).toEqual([
|
||||||
'doCheck-A', 'doCheck-B', 'onChanges-D', 'onInit-D', 'doCheck-D', 'afterContentInit-D',
|
'doCheck-A', 'doCheck-B', 'onInit-D', 'doCheck-D', 'afterContentInit-D',
|
||||||
'afterContentChecked-D', 'afterViewInit-D', 'afterViewChecked-D', 'afterContentChecked-A',
|
'afterContentChecked-D', 'afterViewInit-D', 'afterViewChecked-D', 'afterContentChecked-A',
|
||||||
'afterContentChecked-B', 'afterViewChecked-A', 'afterViewChecked-B'
|
'afterContentChecked-B', 'afterViewChecked-A', 'afterViewChecked-B'
|
||||||
]);
|
]);
|
||||||
|
@ -315,11 +315,10 @@ withEachNg1Version(() => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
it('should bind properties, events', async(() => {
|
||||||
.it('should bind properties, events', async(() => {
|
|
||||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||||
const ng1Module = angular.module('ng1', []).value(
|
const ng1Module =
|
||||||
$EXCEPTION_HANDLER, (err: any) => { throw err; });
|
angular.module('ng1', []).value($EXCEPTION_HANDLER, (err: any) => { throw err; });
|
||||||
|
|
||||||
ng1Module.run(($rootScope: any) => {
|
ng1Module.run(($rootScope: any) => {
|
||||||
$rootScope.name = 'world';
|
$rootScope.name = 'world';
|
||||||
@ -334,8 +333,7 @@ withEachNg1Version(() => {
|
|||||||
selector: 'ng2',
|
selector: 'ng2',
|
||||||
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
||||||
outputs: [
|
outputs: [
|
||||||
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange',
|
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange', 'twoWayBEmitter: twoWayBChange'
|
||||||
'twoWayBEmitter: twoWayBChange'
|
|
||||||
],
|
],
|
||||||
template: 'ignore: {{ignore}}; ' +
|
template: 'ignore: {{ignore}}; ' +
|
||||||
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
|
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
|
||||||
@ -439,8 +437,7 @@ withEachNg1Version(() => {
|
|||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
it('should support two-way binding and event listener', async(() => {
|
||||||
.it('should support two-way binding and event listener', async(() => {
|
|
||||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||||
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
||||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||||
@ -489,8 +486,7 @@ withEachNg1Version(() => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
||||||
.it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
|
||||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -1872,7 +1868,6 @@ withEachNg1Version(() => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-956: refactor onChanges').
|
|
||||||
it('should call `$onChanges()` on binding destination', fakeAsync(() => {
|
it('should call `$onChanges()` on binding destination', fakeAsync(() => {
|
||||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||||
const $onChangesControllerSpyA = jasmine.createSpy('$onChangesControllerA');
|
const $onChangesControllerSpyA = jasmine.createSpy('$onChangesControllerA');
|
||||||
|
@ -22,8 +22,7 @@ withEachNg1Version(() => {
|
|||||||
beforeEach(() => destroyPlatform());
|
beforeEach(() => destroyPlatform());
|
||||||
afterEach(() => destroyPlatform());
|
afterEach(() => destroyPlatform());
|
||||||
|
|
||||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
it('should bind properties, events', async(() => {
|
||||||
.it('should bind properties, events', async(() => {
|
|
||||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||||
$rootScope['name'] = 'world';
|
$rootScope['name'] = 'world';
|
||||||
$rootScope['dataA'] = 'A';
|
$rootScope['dataA'] = 'A';
|
||||||
@ -38,8 +37,7 @@ withEachNg1Version(() => {
|
|||||||
selector: 'ng2',
|
selector: 'ng2',
|
||||||
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
||||||
outputs: [
|
outputs: [
|
||||||
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange',
|
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange', 'twoWayBEmitter: twoWayBChange'
|
||||||
'twoWayBEmitter: twoWayBChange'
|
|
||||||
],
|
],
|
||||||
template: 'ignore: {{ignore}}; ' +
|
template: 'ignore: {{ignore}}; ' +
|
||||||
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
|
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
|
||||||
@ -189,8 +187,7 @@ withEachNg1Version(() => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
it('should support two-way binding and event listener', async(() => {
|
||||||
.it('should support two-way binding and event listener', async(() => {
|
|
||||||
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
||||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||||
$rootScope['value'] = 'world';
|
$rootScope['value'] = 'world';
|
||||||
@ -404,8 +401,7 @@ withEachNg1Version(() => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
||||||
.it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ng2',
|
selector: 'ng2',
|
||||||
template: `
|
template: `
|
||||||
|
@ -721,8 +721,7 @@ withEachNg1Version(() => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
it('should propagate input changes inside the Angular zone', async(() => {
|
||||||
.it('should propagate input changes inside the Angular zone', async(() => {
|
|
||||||
let ng2Component: Ng2Component;
|
let ng2Component: Ng2Component;
|
||||||
|
|
||||||
@Component({selector: 'ng2', template: ''})
|
@Component({selector: 'ng2', template: ''})
|
||||||
@ -748,15 +747,13 @@ withEachNg1Version(() => {
|
|||||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||||
const ng1Module =
|
const ng1Module =
|
||||||
angular.module('ng1', [lazyModuleName])
|
angular.module('ng1', [lazyModuleName])
|
||||||
.directive(
|
.directive('ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
|
||||||
.run(($rootScope: angular.IRootScopeService) => {
|
.run(($rootScope: angular.IRootScopeService) => {
|
||||||
$rootScope.attrVal = 'bar';
|
$rootScope.attrVal = 'bar';
|
||||||
$rootScope.propVal = 'bar';
|
$rootScope.propVal = 'bar';
|
||||||
});
|
});
|
||||||
|
|
||||||
const element =
|
const element = html('<ng2 attr-input="{{ attrVal }}" [prop-input]="propVal"></ng2>');
|
||||||
html('<ng2 attr-input="{{ attrVal }}" [prop-input]="propVal"></ng2>');
|
|
||||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||||
|
|
||||||
@ -943,8 +940,7 @@ withEachNg1Version(() => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
it('should run the lifecycle hooks in the correct order', async(() => {
|
||||||
.it('should run the lifecycle hooks in the correct order', async(() => {
|
|
||||||
const logs: string[] = [];
|
const logs: string[] = [];
|
||||||
let rootScope: angular.IRootScopeService;
|
let rootScope: angular.IRootScopeService;
|
||||||
|
|
||||||
@ -957,8 +953,8 @@ withEachNg1Version(() => {
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
class Ng2Component implements AfterContentChecked,
|
class Ng2Component implements AfterContentChecked,
|
||||||
AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges,
|
AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy,
|
||||||
OnDestroy, OnInit {
|
OnInit {
|
||||||
@Input() value = 'foo';
|
@Input() value = 'foo';
|
||||||
|
|
||||||
ngAfterContentChecked() { this.log('AfterContentChecked'); }
|
ngAfterContentChecked() { this.log('AfterContentChecked'); }
|
||||||
@ -987,8 +983,7 @@ withEachNg1Version(() => {
|
|||||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||||
const ng1Module =
|
const ng1Module =
|
||||||
angular.module('ng1', [lazyModuleName])
|
angular.module('ng1', [lazyModuleName])
|
||||||
.directive(
|
.directive('ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
|
||||||
.run(($rootScope: angular.IRootScopeService) => {
|
.run(($rootScope: angular.IRootScopeService) => {
|
||||||
rootScope = $rootScope;
|
rootScope = $rootScope;
|
||||||
rootScope.value = 'bar';
|
rootScope.value = 'bar';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user