refactor(ivy): get rid of styling cleanup functions outside of styling code (#32591)
Prior to this patch, each time `advance()` would run (or when a templateFn or hostBindings code exits) then the core change detection code would check to see whether the styling data needs to be reset. This patch removes that functionality and places everything inside of the scheduled styling exit function. This means that each time one or more styling bindings run (even if the value hasn't changed) then an exit function will be scheduled and that will do all the cleanup. PR Close #32591
This commit is contained in:
parent
4f41473048
commit
a2e890e4f7
|
@ -12,7 +12,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime": 1440,
|
"runtime": 1440,
|
||||||
"main": 13604,
|
"main": 13415,
|
||||||
"polyfills": 45340
|
"polyfills": 45340
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {assertDataInRange, assertGreaterThan} from '../../util/assert';
|
||||||
import {executeCheckHooks, executeInitAndCheckHooks} from '../hooks';
|
import {executeCheckHooks, executeInitAndCheckHooks} from '../hooks';
|
||||||
import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, TVIEW} from '../interfaces/view';
|
import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, TVIEW} from '../interfaces/view';
|
||||||
import {ActiveElementFlags, executeElementExitFn, getCheckNoChangesMode, getLView, getSelectedIndex, hasActiveElementFlag, setSelectedIndex} from '../state';
|
import {ActiveElementFlags, executeElementExitFn, getCheckNoChangesMode, getLView, getSelectedIndex, hasActiveElementFlag, setSelectedIndex} from '../state';
|
||||||
import {resetStylingState} from '../styling_next/state';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,10 +75,6 @@ export function selectIndexInternal(lView: LView, index: number, checkNoChangesM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasActiveElementFlag(ActiveElementFlags.ResetStylesOnExit)) {
|
|
||||||
resetStylingState();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We must set the selected index *after* running the hooks, because hooks may have side-effects
|
// We must set the selected index *after* running the hooks, because hooks may have side-effects
|
||||||
// that cause other template functions to run, thus updating the selected index, which is global
|
// that cause other template functions to run, thus updating the selected index, which is global
|
||||||
// state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
|
// state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
|
||||||
|
|
|
@ -32,7 +32,6 @@ import {assertNodeOfPossibleTypes} from '../node_assert';
|
||||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||||
import {ActiveElementFlags, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, namespaceHTMLInternal, selectView, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
import {ActiveElementFlags, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, namespaceHTMLInternal, selectView, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
||||||
import {renderStylingMap} from '../styling_next/bindings';
|
import {renderStylingMap} from '../styling_next/bindings';
|
||||||
import {resetStylingState} from '../styling_next/state';
|
|
||||||
import {NO_CHANGE} from '../tokens';
|
import {NO_CHANGE} from '../tokens';
|
||||||
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../util/attrs_utils';
|
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../util/attrs_utils';
|
||||||
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
|
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
|
||||||
|
@ -505,9 +504,6 @@ function executeTemplate<T>(
|
||||||
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
||||||
executeElementExitFn();
|
executeElementExitFn();
|
||||||
}
|
}
|
||||||
if (hasActiveElementFlag(ActiveElementFlags.ResetStylesOnExit)) {
|
|
||||||
resetStylingState();
|
|
||||||
}
|
|
||||||
setSelectedIndex(prevSelectedIndex);
|
setSelectedIndex(prevSelectedIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,7 @@ import {assertDefined} from '../util/assert';
|
||||||
import {assertLViewOrUndefined} from './assert';
|
import {assertLViewOrUndefined} from './assert';
|
||||||
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
||||||
import {TElementNode, TNode, TViewNode} from './interfaces/node';
|
import {TElementNode, TNode, TViewNode} from './interfaces/node';
|
||||||
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TVIEW} from './interfaces/view';
|
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState} from './interfaces/view';
|
||||||
import {resetStylingState} from './styling_next/state';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,8 +143,7 @@ let activeDirectiveId = 0;
|
||||||
export const enum ActiveElementFlags {
|
export const enum ActiveElementFlags {
|
||||||
Initial = 0b00,
|
Initial = 0b00,
|
||||||
RunExitFn = 0b01,
|
RunExitFn = 0b01,
|
||||||
ResetStylesOnExit = 0b10,
|
Size = 1,
|
||||||
Size = 2,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,9 +172,6 @@ export function setActiveHostElement(elementIndex: number | null = null) {
|
||||||
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
||||||
executeElementExitFn();
|
executeElementExitFn();
|
||||||
}
|
}
|
||||||
if (hasActiveElementFlag(ActiveElementFlags.ResetStylesOnExit)) {
|
|
||||||
resetStylingState();
|
|
||||||
}
|
|
||||||
setSelectedIndex(elementIndex === null ? -1 : elementIndex);
|
setSelectedIndex(elementIndex === null ? -1 : elementIndex);
|
||||||
activeDirectiveId = 0;
|
activeDirectiveId = 0;
|
||||||
}
|
}
|
||||||
|
@ -390,6 +385,10 @@ export function setCurrentQueryIndex(value: number): void {
|
||||||
* @returns the previously active lView;
|
* @returns the previously active lView;
|
||||||
*/
|
*/
|
||||||
export function selectView(newView: LView, hostTNode: TElementNode | TViewNode | null): LView {
|
export function selectView(newView: LView, hostTNode: TElementNode | TViewNode | null): LView {
|
||||||
|
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
||||||
|
executeElementExitFn();
|
||||||
|
}
|
||||||
|
|
||||||
ngDevMode && assertLViewOrUndefined(newView);
|
ngDevMode && assertLViewOrUndefined(newView);
|
||||||
const oldView = lView;
|
const oldView = lView;
|
||||||
|
|
||||||
|
|
|
@ -405,19 +405,23 @@ export function flushStyling(
|
||||||
if (!isContextLocked(stylesContext, hostBindingsMode)) {
|
if (!isContextLocked(stylesContext, hostBindingsMode)) {
|
||||||
lockAndFinalizeContext(stylesContext, hostBindingsMode);
|
lockAndFinalizeContext(stylesContext, hostBindingsMode);
|
||||||
}
|
}
|
||||||
|
if (state.stylesBitMask !== 0) {
|
||||||
applyStylingViaContext(
|
applyStylingViaContext(
|
||||||
stylesContext, renderer, element, data, state.stylesBitMask, setStyle, styleSanitizer,
|
stylesContext, renderer, element, data, state.stylesBitMask, setStyle, styleSanitizer,
|
||||||
hostBindingsMode);
|
hostBindingsMode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (classesContext) {
|
if (classesContext) {
|
||||||
if (!isContextLocked(classesContext, hostBindingsMode)) {
|
if (!isContextLocked(classesContext, hostBindingsMode)) {
|
||||||
lockAndFinalizeContext(classesContext, hostBindingsMode);
|
lockAndFinalizeContext(classesContext, hostBindingsMode);
|
||||||
}
|
}
|
||||||
|
if (state.classesBitMask !== 0) {
|
||||||
applyStylingViaContext(
|
applyStylingViaContext(
|
||||||
classesContext, renderer, element, data, state.classesBitMask, setClass, null,
|
classesContext, renderer, element, data, state.classesBitMask, setClass, null,
|
||||||
hostBindingsMode);
|
hostBindingsMode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resetStylingState();
|
resetStylingState();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {setInputsForProperty} from '../instructions/shared';
|
||||||
import {AttributeMarker, TAttributes, TNode, TNodeType} from '../interfaces/node';
|
import {AttributeMarker, TAttributes, TNode, TNodeType} from '../interfaces/node';
|
||||||
import {RElement} from '../interfaces/renderer';
|
import {RElement} from '../interfaces/renderer';
|
||||||
import {BINDING_INDEX, LView, RENDERER} from '../interfaces/view';
|
import {BINDING_INDEX, LView, RENDERER} from '../interfaces/view';
|
||||||
import {ActiveElementFlags, getActiveDirectiveId, getCurrentStyleSanitizer, getLView, getSelectedIndex, setActiveElementFlag, setCurrentStyleSanitizer, setElementExitFn} from '../state';
|
import {getActiveDirectiveId, getCurrentStyleSanitizer, getLView, getSelectedIndex, setCurrentStyleSanitizer, setElementExitFn} from '../state';
|
||||||
import {NO_CHANGE} from '../tokens';
|
import {NO_CHANGE} from '../tokens';
|
||||||
import {renderStringify} from '../util/misc_utils';
|
import {renderStringify} from '../util/misc_utils';
|
||||||
import {getNativeByTNode, getTNode} from '../util/view_utils';
|
import {getNativeByTNode, getTNode} from '../util/view_utils';
|
||||||
|
@ -186,10 +186,7 @@ function stylingProp(
|
||||||
value as string | SafeValue | null, sanitizer);
|
value as string | SafeValue | null, sanitizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updated) {
|
setElementExitFn(stylingApply);
|
||||||
setElementExitFn(applyStyling);
|
|
||||||
}
|
|
||||||
markStylingStateAsDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return updated;
|
return updated;
|
||||||
|
@ -331,6 +328,9 @@ function _stylingMap(
|
||||||
renderer, context, native, lView, bindingIndex, stylingMapArr as StylingMapArray,
|
renderer, context, native, lView, bindingIndex, stylingMapArr as StylingMapArray,
|
||||||
isClassBased ? setClass : setStyle, sanitizer, valueHasChanged);
|
isClassBased ? setClass : setStyle, sanitizer, valueHasChanged);
|
||||||
} else {
|
} else {
|
||||||
|
updated = valueHasChanged;
|
||||||
|
activateStylingMapFeature();
|
||||||
|
|
||||||
// Context Resolution (or first update) Case: save the map value
|
// Context Resolution (or first update) Case: save the map value
|
||||||
// and defer to the context to flush and apply the style/class binding
|
// and defer to the context to flush and apply the style/class binding
|
||||||
// value to the element.
|
// value to the element.
|
||||||
|
@ -344,15 +344,9 @@ function _stylingMap(
|
||||||
valueHasChanged);
|
valueHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueHasChanged) {
|
setElementExitFn(stylingApply);
|
||||||
updated = true;
|
|
||||||
setElementExitFn(applyStyling);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activateStylingMapFeature();
|
|
||||||
}
|
|
||||||
|
|
||||||
markStylingStateAsDirty();
|
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,9 +378,9 @@ function updateDirectiveInputValue(
|
||||||
const initialValue = getInitialStylingValue(context);
|
const initialValue = getInitialStylingValue(context);
|
||||||
const value = normalizeStylingDirectiveInputValue(initialValue, newValue, isClassBased);
|
const value = normalizeStylingDirectiveInputValue(initialValue, newValue, isClassBased);
|
||||||
setInputsForProperty(lView, inputs, value);
|
setInputsForProperty(lView, inputs, value);
|
||||||
|
setElementExitFn(stylingApply);
|
||||||
}
|
}
|
||||||
setValue(lView, bindingIndex, newValue);
|
setValue(lView, bindingIndex, newValue);
|
||||||
setElementExitFn(applyStyling);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,17 +417,17 @@ function normalizeStylingDirectiveInputValue(
|
||||||
* in this file. When called it will flush all style and class bindings to the element
|
* in this file. When called it will flush all style and class bindings to the element
|
||||||
* via the context resolution algorithm.
|
* via the context resolution algorithm.
|
||||||
*/
|
*/
|
||||||
function applyStyling(): void {
|
function stylingApply(): void {
|
||||||
const elementIndex = getSelectedIndex();
|
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
|
const elementIndex = getSelectedIndex();
|
||||||
const tNode = getTNode(elementIndex, lView);
|
const tNode = getTNode(elementIndex, lView);
|
||||||
const renderer = getRenderer(tNode, lView);
|
|
||||||
const native = getNativeByTNode(tNode, lView) as RElement;
|
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||||
|
const directiveIndex = getActiveDirectiveId();
|
||||||
|
const renderer = getRenderer(tNode, lView);
|
||||||
const sanitizer = getCurrentStyleSanitizer();
|
const sanitizer = getCurrentStyleSanitizer();
|
||||||
const classesContext = isStylingContext(tNode.classes) ? tNode.classes as TStylingContext : null;
|
const classesContext = isStylingContext(tNode.classes) ? tNode.classes as TStylingContext : null;
|
||||||
const stylesContext = isStylingContext(tNode.styles) ? tNode.styles as TStylingContext : null;
|
const stylesContext = isStylingContext(tNode.styles) ? tNode.styles as TStylingContext : null;
|
||||||
flushStyling(
|
flushStyling(renderer, lView, classesContext, stylesContext, native, directiveIndex, sanitizer);
|
||||||
renderer, lView, classesContext, stylesContext, native, getActiveDirectiveId(), sanitizer);
|
|
||||||
setCurrentStyleSanitizer(null);
|
setCurrentStyleSanitizer(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,10 +532,6 @@ function resolveStylePropValue(
|
||||||
return resolvedValue;
|
return resolvedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function markStylingStateAsDirty(): void {
|
|
||||||
setActiveElementFlag(ActiveElementFlags.ResetStylesOnExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the style/class binding being applied was executed within a host bindings
|
* Whether or not the style/class binding being applied was executed within a host bindings
|
||||||
* function.
|
* function.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* 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 {Component, Directive, HostBinding, Input, ViewChild} from '@angular/core';
|
import {Component, ComponentFactoryResolver, ComponentRef, Directive, HostBinding, Input, NgModule, ViewChild, ViewContainerRef} from '@angular/core';
|
||||||
import {DebugNode, LViewDebug, toDebug} from '@angular/core/src/render3/instructions/lview_debug';
|
import {DebugNode, LViewDebug, toDebug} from '@angular/core/src/render3/instructions/lview_debug';
|
||||||
import {loadLContextFromNode} from '@angular/core/src/render3/util/discovery_utils';
|
import {loadLContextFromNode} from '@angular/core/src/render3/util/discovery_utils';
|
||||||
import {ngDevModeResetPerfCounters as resetStylingCounters} from '@angular/core/src/util/ng_dev_mode';
|
import {ngDevModeResetPerfCounters as resetStylingCounters} from '@angular/core/src/util/ng_dev_mode';
|
||||||
|
@ -1168,6 +1168,175 @@ describe('new styling integration', () => {
|
||||||
expect(div.classList.contains('foo')).toBeTruthy();
|
expect(div.classList.contains('foo')).toBeTruthy();
|
||||||
expect(div.classList.contains('bar')).toBeTruthy();
|
expect(div.classList.contains('bar')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow detectChanges to be run in a property change that causes additional styling to be rendered',
|
||||||
|
() => {
|
||||||
|
@Component({
|
||||||
|
selector: 'child',
|
||||||
|
template: `
|
||||||
|
<div [class.ready-child]="readyTpl"></div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class ChildCmp {
|
||||||
|
readyTpl = false;
|
||||||
|
|
||||||
|
@HostBinding('class.ready-host')
|
||||||
|
readyHost = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'parent',
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<div #template></div>
|
||||||
|
<p>{{prop}}</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
host: {
|
||||||
|
'[style.color]': 'color',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
class ParentCmp {
|
||||||
|
private _prop = '';
|
||||||
|
|
||||||
|
@ViewChild('template', {read: ViewContainerRef, static: false})
|
||||||
|
vcr: ViewContainerRef = null !;
|
||||||
|
|
||||||
|
private child: ComponentRef<ChildCmp> = null !;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set prop(value: string) {
|
||||||
|
this._prop = value;
|
||||||
|
if (this.child && value === 'go') {
|
||||||
|
this.child.instance.readyHost = true;
|
||||||
|
this.child.instance.readyTpl = true;
|
||||||
|
this.child.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get prop() { return this._prop; }
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp);
|
||||||
|
this.child = this.vcr.createComponent(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `<parent [prop]="prop"></parent>`,
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
prop = 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
entryComponents: [ChildCmp],
|
||||||
|
declarations: [ChildCmp],
|
||||||
|
})
|
||||||
|
class ChildCmpModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [App, ParentCmp], imports: [ChildCmpModule]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges(false);
|
||||||
|
|
||||||
|
let readyHost = fixture.nativeElement.querySelector('.ready-host');
|
||||||
|
let readyChild = fixture.nativeElement.querySelector('.ready-child');
|
||||||
|
|
||||||
|
expect(readyHost).toBeFalsy();
|
||||||
|
expect(readyChild).toBeFalsy();
|
||||||
|
|
||||||
|
fixture.componentInstance.prop = 'go';
|
||||||
|
fixture.detectChanges(false);
|
||||||
|
|
||||||
|
readyHost = fixture.nativeElement.querySelector('.ready-host');
|
||||||
|
readyChild = fixture.nativeElement.querySelector('.ready-child');
|
||||||
|
expect(readyHost).toBeTruthy();
|
||||||
|
expect(readyChild).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow detectChanges to be run in a hook that causes additional styling to be rendered',
|
||||||
|
() => {
|
||||||
|
@Component({
|
||||||
|
selector: 'child',
|
||||||
|
template: `
|
||||||
|
<div [class.ready-child]="readyTpl"></div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class ChildCmp {
|
||||||
|
readyTpl = false;
|
||||||
|
|
||||||
|
@HostBinding('class.ready-host')
|
||||||
|
readyHost = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'parent',
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<div #template></div>
|
||||||
|
<p>{{prop}}</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class ParentCmp {
|
||||||
|
updateChild = false;
|
||||||
|
|
||||||
|
@ViewChild('template', {read: ViewContainerRef, static: false})
|
||||||
|
vcr: ViewContainerRef = null !;
|
||||||
|
|
||||||
|
private child: ComponentRef<ChildCmp> = null !;
|
||||||
|
|
||||||
|
ngDoCheck() {
|
||||||
|
if (this.updateChild) {
|
||||||
|
this.child.instance.readyHost = true;
|
||||||
|
this.child.instance.readyTpl = true;
|
||||||
|
this.child.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp);
|
||||||
|
this.child = this.vcr.createComponent(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `<parent #parent></parent>`,
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild('parent', {static: true}) public parent: ParentCmp|null = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
entryComponents: [ChildCmp],
|
||||||
|
declarations: [ChildCmp],
|
||||||
|
})
|
||||||
|
class ChildCmpModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [App, ParentCmp], imports: [ChildCmpModule]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges(false);
|
||||||
|
|
||||||
|
let readyHost = fixture.nativeElement.querySelector('.ready-host');
|
||||||
|
let readyChild = fixture.nativeElement.querySelector('.ready-child');
|
||||||
|
expect(readyHost).toBeFalsy();
|
||||||
|
expect(readyChild).toBeFalsy();
|
||||||
|
|
||||||
|
const parent = fixture.componentInstance.parent !;
|
||||||
|
parent.updateChild = true;
|
||||||
|
fixture.detectChanges(false);
|
||||||
|
|
||||||
|
readyHost = fixture.nativeElement.querySelector('.ready-host');
|
||||||
|
readyChild = fixture.nativeElement.querySelector('.ready-child');
|
||||||
|
expect(readyHost).toBeTruthy();
|
||||||
|
expect(readyChild).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function assertStyleCounters(countForSet: number, countForRemove: number) {
|
function assertStyleCounters(countForSet: number, countForRemove: number) {
|
||||||
|
|
|
@ -167,9 +167,6 @@
|
||||||
{
|
{
|
||||||
"name": "_selectedIndex"
|
"name": "_selectedIndex"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "_state"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "addComponentLogic"
|
"name": "addComponentLogic"
|
||||||
},
|
},
|
||||||
|
@ -590,9 +587,6 @@
|
||||||
{
|
{
|
||||||
"name": "resetPreOrderHookFlags"
|
"name": "resetPreOrderHookFlags"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "resetStylingState"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "resolveDirectives"
|
"name": "resolveDirectives"
|
||||||
},
|
},
|
||||||
|
|
|
@ -146,9 +146,6 @@
|
||||||
{
|
{
|
||||||
"name": "_selectedIndex"
|
"name": "_selectedIndex"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "_state"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "addToViewTree"
|
"name": "addToViewTree"
|
||||||
},
|
},
|
||||||
|
@ -434,9 +431,6 @@
|
||||||
{
|
{
|
||||||
"name": "resetPreOrderHookFlags"
|
"name": "resetPreOrderHookFlags"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "resetStylingState"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "selectIndexInternal"
|
"name": "selectIndexInternal"
|
||||||
},
|
},
|
||||||
|
|
|
@ -467,9 +467,6 @@
|
||||||
{
|
{
|
||||||
"name": "applyProjectionRecursive"
|
"name": "applyProjectionRecursive"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "applyStyling"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "applyStylingValue"
|
"name": "applyStylingValue"
|
||||||
},
|
},
|
||||||
|
@ -914,9 +911,6 @@
|
||||||
{
|
{
|
||||||
"name": "hasConfig"
|
"name": "hasConfig"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "hasDirectives"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "hasParentInjector"
|
"name": "hasParentInjector"
|
||||||
},
|
},
|
||||||
|
@ -945,7 +939,7 @@
|
||||||
"name": "initNodeFlags"
|
"name": "initNodeFlags"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "initializeTNodeInputs"
|
"name": "initializeInputAndOutputAliases"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "injectElementRef"
|
"name": "injectElementRef"
|
||||||
|
@ -1106,9 +1100,6 @@
|
||||||
{
|
{
|
||||||
"name": "markDirtyIfOnPush"
|
"name": "markDirtyIfOnPush"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "markStylingStateAsDirty"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "markViewDirty"
|
"name": "markViewDirty"
|
||||||
},
|
},
|
||||||
|
@ -1355,6 +1346,9 @@
|
||||||
{
|
{
|
||||||
"name": "stringifyForError"
|
"name": "stringifyForError"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "stylingApply"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "stylingMapToString"
|
"name": "stylingMapToString"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue