fix(ivy): blueprints should be synced whenever they are off (#27281)
PR Close #27281
This commit is contained in:
parent
a7ba05ad82
commit
d0e8020506
|
@ -62,7 +62,6 @@ const enum BindingDirection {
|
||||||
*/
|
*/
|
||||||
export function refreshDescendantViews(viewData: LViewData, rf: RenderFlags | null) {
|
export function refreshDescendantViews(viewData: LViewData, rf: RenderFlags | null) {
|
||||||
const tView = getTView();
|
const tView = getTView();
|
||||||
const parentFirstTemplatePass = getFirstTemplatePass();
|
|
||||||
|
|
||||||
// This needs to be set before children are processed to support recursive components
|
// This needs to be set before children are processed to support recursive components
|
||||||
tView.firstTemplatePass = false;
|
tView.firstTemplatePass = false;
|
||||||
|
@ -91,7 +90,7 @@ export function refreshDescendantViews(viewData: LViewData, rf: RenderFlags | nu
|
||||||
setHostBindings(tView, viewData);
|
setHostBindings(tView, viewData);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshChildComponents(tView.components, parentFirstTemplatePass, rf);
|
refreshChildComponents(tView.components, rf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,11 +146,10 @@ function refreshContentQueries(tView: TView): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Refreshes child components in the current view. */
|
/** Refreshes child components in the current view. */
|
||||||
function refreshChildComponents(
|
function refreshChildComponents(components: number[] | null, rf: RenderFlags | null): void {
|
||||||
components: number[] | null, parentFirstTemplatePass: boolean, rf: RenderFlags | null): void {
|
|
||||||
if (components != null) {
|
if (components != null) {
|
||||||
for (let i = 0; i < components.length; i++) {
|
for (let i = 0; i < components.length; i++) {
|
||||||
componentRefresh(components[i], parentFirstTemplatePass, rf);
|
componentRefresh(components[i], rf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2068,16 +2066,16 @@ export function embeddedViewEnd(): void {
|
||||||
* Refreshes components by entering the component view and processing its bindings, queries, etc.
|
* Refreshes components by entering the component view and processing its bindings, queries, etc.
|
||||||
*
|
*
|
||||||
* @param adjustedElementIndex Element index in LViewData[] (adjusted for HEADER_OFFSET)
|
* @param adjustedElementIndex Element index in LViewData[] (adjusted for HEADER_OFFSET)
|
||||||
|
* @param rf The render flags that should be used to process this template
|
||||||
*/
|
*/
|
||||||
export function componentRefresh<T>(
|
export function componentRefresh<T>(adjustedElementIndex: number, rf: RenderFlags | null): void {
|
||||||
adjustedElementIndex: number, parentFirstTemplatePass: boolean, rf: RenderFlags | null): void {
|
|
||||||
ngDevMode && assertDataInRange(adjustedElementIndex);
|
ngDevMode && assertDataInRange(adjustedElementIndex);
|
||||||
const hostView = getComponentViewByIndex(adjustedElementIndex, getViewData());
|
const hostView = getComponentViewByIndex(adjustedElementIndex, getViewData());
|
||||||
ngDevMode && assertNodeType(getTView().data[adjustedElementIndex] as TNode, TNodeType.Element);
|
ngDevMode && assertNodeType(getTView().data[adjustedElementIndex] as TNode, TNodeType.Element);
|
||||||
|
|
||||||
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
|
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
|
||||||
if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
|
if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
|
||||||
parentFirstTemplatePass && syncViewWithBlueprint(hostView);
|
syncViewWithBlueprint(hostView);
|
||||||
detectChangesInternal(hostView, hostView[CONTEXT], rf);
|
detectChangesInternal(hostView, hostView[CONTEXT], rf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,9 +230,6 @@
|
||||||
{
|
{
|
||||||
"name": "extractPipeDef"
|
"name": "extractPipeDef"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "firstTemplatePass"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "generateExpandoInstructionBlock"
|
"name": "generateExpandoInstructionBlock"
|
||||||
},
|
},
|
||||||
|
@ -263,9 +260,6 @@
|
||||||
{
|
{
|
||||||
"name": "getFirstParentNative"
|
"name": "getFirstParentNative"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getFirstTemplatePass"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getHighestElementContainer"
|
"name": "getHighestElementContainer"
|
||||||
},
|
},
|
||||||
|
|
|
@ -782,9 +782,6 @@
|
||||||
{
|
{
|
||||||
"name": "findViaComponent"
|
"name": "findViaComponent"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "firstTemplatePass"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "flattenUnsubscriptionErrors"
|
"name": "flattenUnsubscriptionErrors"
|
||||||
},
|
},
|
||||||
|
@ -860,9 +857,6 @@
|
||||||
{
|
{
|
||||||
"name": "getFirstParentNative"
|
"name": "getFirstParentNative"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getFirstTemplatePass"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getHighestElementContainer"
|
"name": "getHighestElementContainer"
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
import {NgForOfContext} from '@angular/common';
|
import {NgForOfContext} from '@angular/common';
|
||||||
|
|
||||||
import {AttributeMarker, defineComponent, templateRefExtractor} from '../../src/render3/index';
|
import {AttributeMarker, defineComponent, element, templateRefExtractor} from '../../src/render3/index';
|
||||||
import {bind, template, elementEnd, elementProperty, elementStart, interpolation1, interpolation2, interpolation3, interpolationV, listener, load, nextContext, text, textBinding, elementContainerStart, elementContainerEnd, reference} from '../../src/render3/instructions';
|
import {bind, template, elementEnd, elementProperty, elementStart, interpolation1, interpolation2, interpolation3, interpolationV, listener, load, nextContext, text, textBinding, elementContainerStart, elementContainerEnd, reference} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {getCurrentView, restoreView} from '../../src/render3/state';
|
import {getCurrentView, restoreView} from '../../src/render3/state';
|
||||||
|
|
||||||
import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def';
|
import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def';
|
||||||
import {ComponentFixture} from './render_util';
|
import {ComponentFixture, createDirective, getDirectiveOnNode} from './render_util';
|
||||||
|
|
||||||
describe('@angular/common integration', () => {
|
describe('@angular/common integration', () => {
|
||||||
|
|
||||||
|
@ -131,6 +131,75 @@ describe('@angular/common integration', () => {
|
||||||
.toEqual('<ul><li>0 of 3: first</li><li>1 of 3: middle</li><li>2 of 3: second</li></ul>');
|
.toEqual('<ul><li>0 of 3: first</li><li>1 of 3: middle</li><li>2 of 3: second</li></ul>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should instantiate directives inside directives properly in an ngFor', () => {
|
||||||
|
let dirs: any[] = [];
|
||||||
|
|
||||||
|
const Dir = createDirective('dir');
|
||||||
|
|
||||||
|
class Comp {
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: Comp,
|
||||||
|
selectors: [['comp']],
|
||||||
|
factory: () => new Comp(),
|
||||||
|
consts: 2,
|
||||||
|
vars: 0,
|
||||||
|
template: (rf: RenderFlags, cmp: Comp) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['dir', '']);
|
||||||
|
{ text(1, 'comp text'); }
|
||||||
|
elementEnd();
|
||||||
|
// testing only
|
||||||
|
dirs.push(getDirectiveOnNode(0));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directives: [Dir]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ngForTemplate(rf: RenderFlags, ctx: NgForOfContext<string>) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
element(0, 'comp');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** <comp *ngFor="let row of rows"></comp> */
|
||||||
|
class MyApp {
|
||||||
|
rows: string[] = ['first', 'second'];
|
||||||
|
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: MyApp,
|
||||||
|
factory: () => new MyApp(),
|
||||||
|
selectors: [['my-app']],
|
||||||
|
consts: 1,
|
||||||
|
vars: 1,
|
||||||
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
template(0, ngForTemplate, 1, 0, undefined, ['ngForOf', '']);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementProperty(0, 'ngForOf', bind(ctx.rows));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directives: () => [NgForOf, Comp, Dir]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(MyApp);
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<comp><div dir="">comp text</div></comp><comp><div dir="">comp text</div></comp>');
|
||||||
|
expect(dirs.length).toBe(2);
|
||||||
|
expect(dirs[0] instanceof Dir).toBe(true);
|
||||||
|
expect(dirs[1] instanceof Dir).toBe(true);
|
||||||
|
|
||||||
|
fixture.component.rows.push('third');
|
||||||
|
fixture.update();
|
||||||
|
expect(dirs.length).toBe(3);
|
||||||
|
expect(dirs[2] instanceof Dir).toBe(true);
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<comp><div dir="">comp text</div></comp><comp><div dir="">comp text</div></comp><comp><div dir="">comp text</div></comp>');
|
||||||
|
});
|
||||||
|
|
||||||
it('should retain parent view listeners when the NgFor destroy views', () => {
|
it('should retain parent view listeners when the NgFor destroy views', () => {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue