refactor(ivy): ViewRef needs embededViewRef declaration (#33074)
PR Close #33074
This commit is contained in:
parent
f1ffd57105
commit
5632424d04
|
@ -122,7 +122,7 @@ export function createTemplateRef<T>(
|
|||
|
||||
renderView(lView, embeddedTView, context);
|
||||
|
||||
const viewRef = new ViewRef(lView, context, -1);
|
||||
const viewRef = new ViewRef<T>(lView);
|
||||
viewRef._tViewNode = lView[T_HOST] as TViewNode;
|
||||
return viewRef;
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ export function createContainerRef(
|
|||
|
||||
const wasDetached =
|
||||
view && removeFromArray(this._lContainer[VIEW_REFS] !, adjustedIdx) != null;
|
||||
return wasDetached ? new ViewRef(view !, view ![CONTEXT], -1) : null;
|
||||
return wasDetached ? new ViewRef(view !) : null;
|
||||
}
|
||||
|
||||
private _adjustIndex(index?: number, shift: number = 0) {
|
||||
|
@ -370,22 +370,27 @@ export function injectChangeDetectorRef(isPipe = false): ViewEngine_ChangeDetect
|
|||
/**
|
||||
* Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias).
|
||||
*
|
||||
* @param hostTNode The node that is requesting a ChangeDetectorRef
|
||||
* @param hostView The view to which the node belongs
|
||||
* @param tNode The node that is requesting a ChangeDetectorRef
|
||||
* @param lView The view to which the node belongs
|
||||
* @param isPipe Whether the view is being injected into a pipe.
|
||||
* @returns The ChangeDetectorRef to use
|
||||
*/
|
||||
function createViewRef(
|
||||
hostTNode: TNode, hostView: LView, isPipe: boolean): ViewEngine_ChangeDetectorRef {
|
||||
if (isComponentHost(hostTNode) && !isPipe) {
|
||||
const componentIndex = hostTNode.directiveStart;
|
||||
const componentView = getComponentLViewByIndex(hostTNode.index, hostView);
|
||||
return new ViewRef(componentView, null, componentIndex);
|
||||
function createViewRef(tNode: TNode, lView: LView, isPipe: boolean): ViewEngine_ChangeDetectorRef {
|
||||
// `isComponentView` will be true for Component and Directives (but not for Pipes).
|
||||
// See https://github.com/angular/angular/pull/33072 for proper fix
|
||||
const isComponentView = !isPipe && isComponentHost(tNode);
|
||||
if (isComponentView) {
|
||||
// The LView represents the location where the component is declared.
|
||||
// Instead we want the LView for the component View and so we need to look it up.
|
||||
const componentView = getComponentLViewByIndex(tNode.index, lView); // look down
|
||||
return new ViewRef(componentView, componentView);
|
||||
} else if (
|
||||
hostTNode.type === TNodeType.Element || hostTNode.type === TNodeType.Container ||
|
||||
hostTNode.type === TNodeType.ElementContainer) {
|
||||
const hostComponentView = findComponentView(hostView);
|
||||
return new ViewRef(hostComponentView, hostComponentView[CONTEXT], -1);
|
||||
tNode.type === TNodeType.Element || tNode.type === TNodeType.Container ||
|
||||
tNode.type === TNodeType.ElementContainer) {
|
||||
// The LView represents the location where the injection is requested from.
|
||||
// We need to locate the containing LView (in case where the `lView` is an embedded view)
|
||||
const hostComponentView = findComponentView(lView); // look up
|
||||
return new ViewRef(hostComponentView, lView);
|
||||
}
|
||||
return null !;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEn
|
|||
|
||||
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions/shared';
|
||||
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
|
||||
import {FLAGS, HOST, LView, LViewFlags, T_HOST} from './interfaces/view';
|
||||
import {CONTEXT, FLAGS, HOST, LView, LViewFlags, T_HOST} from './interfaces/view';
|
||||
import {destroyLView, renderDetachView} from './node_manipulation';
|
||||
import {findComponentView, getLViewParent} from './util/view_traversal_utils';
|
||||
import {getNativeByTNode, getNativeByTNodeOrNull} from './util/view_utils';
|
||||
|
@ -35,11 +35,6 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
|||
*/
|
||||
public _tViewNode: TViewNode|null = null;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public _lView: LView;
|
||||
|
||||
get rootNodes(): any[] {
|
||||
if (this._lView[HOST] == null) {
|
||||
const tView = this._lView[T_HOST] as TViewNode;
|
||||
|
@ -48,11 +43,29 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
|||
return [];
|
||||
}
|
||||
|
||||
constructor(_lView: LView, private _context: T|null, private _componentIndex: number) {
|
||||
this._lView = _lView;
|
||||
}
|
||||
constructor(
|
||||
/**
|
||||
* This represents `LView` associated with the component when ViewRef is a ChangeDetectorRef.
|
||||
*
|
||||
* When ViewRef is created for a dynamic component, this also represents the `LView` for the
|
||||
* component.
|
||||
*
|
||||
* For a "regular" ViewRef created for an embedded view, this is the `LView` for the embedded
|
||||
* view.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public _lView: LView,
|
||||
|
||||
get context(): T { return this._context ? this._context : this._lookUpContext(); }
|
||||
/**
|
||||
* This represents the `LView` associated with the point where `ChangeDetectorRef` was
|
||||
* requested.
|
||||
*
|
||||
* This may be different from `_lView` if the `_cdRefInjectingView` is an embedded view.
|
||||
*/
|
||||
private _cdRefInjectingView?: LView) {}
|
||||
|
||||
get context(): T { return this._lView[CONTEXT] as T; }
|
||||
|
||||
get destroyed(): boolean {
|
||||
return (this._lView[FLAGS] & LViewFlags.Destroyed) === LViewFlags.Destroyed;
|
||||
|
@ -109,7 +122,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
markForCheck(): void { markViewDirty(this._lView); }
|
||||
markForCheck(): void { markViewDirty(this._cdRefInjectingView || this._lView); }
|
||||
|
||||
/**
|
||||
* Detaches the view from the change detection tree.
|
||||
|
@ -273,15 +286,11 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
|||
}
|
||||
this._appRef = appRef;
|
||||
}
|
||||
|
||||
private _lookUpContext(): T {
|
||||
return this._context = getLViewParent(this._lView) ![this._componentIndex] as T;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export class RootViewRef<T> extends ViewRef<T> {
|
||||
constructor(public _view: LView) { super(_view, null, -1); }
|
||||
constructor(public _view: LView) { super(_view); }
|
||||
|
||||
detectChanges(): void { detectChangesInRootView(this._view); }
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import {CommonModule} from '@angular/common';
|
|||
import {ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, DoCheck, EmbeddedViewRef, ErrorHandler, Input, NgModule, OnInit, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {BehaviorSubject} from 'rxjs';
|
||||
|
||||
describe('change detection', () => {
|
||||
|
||||
|
@ -913,6 +914,41 @@ describe('change detection', () => {
|
|||
expect(fixture.nativeElement.textContent).toEqual('two - two');
|
||||
});
|
||||
|
||||
it('async pipe should trigger CD for embedded views where the declaration and insertion views are different',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'insertion',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: ` <ng-container [ngTemplateOutlet]="template"> </ng-container> `
|
||||
})
|
||||
class Insertion {
|
||||
@Input() template !: TemplateRef<{}>;
|
||||
}
|
||||
|
||||
// This component uses async pipe (which calls markForCheck) in a view that has different
|
||||
// insertion and declaration views.
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<insertion [template]="ref"></insertion>
|
||||
<ng-template #ref>
|
||||
<span>{{value | async}}</span>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class Declaration {
|
||||
value = new BehaviorSubject('initial value');
|
||||
}
|
||||
|
||||
const fixture = TestBed.configureTestingModule({declarations: [Insertion, Declaration]})
|
||||
.createComponent(Declaration);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.textContent).toContain('initial value');
|
||||
fixture.componentInstance.value.next('new value');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.textContent).toContain('new value');
|
||||
});
|
||||
|
||||
// TODO(kara): add test for dynamic views once bug fix is in
|
||||
});
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@
|
|||
"name": "getComponentDef"
|
||||
},
|
||||
{
|
||||
"name": "getComponentViewByIndex"
|
||||
"name": "getComponentLViewByIndex"
|
||||
},
|
||||
{
|
||||
"name": "getContainerRenderParent"
|
||||
|
|
|
@ -225,7 +225,7 @@
|
|||
"name": "getComponentDef"
|
||||
},
|
||||
{
|
||||
"name": "getComponentViewByIndex"
|
||||
"name": "getComponentLViewByIndex"
|
||||
},
|
||||
{
|
||||
"name": "getContainerRenderParent"
|
||||
|
|
|
@ -621,7 +621,7 @@
|
|||
"name": "getComponentDef"
|
||||
},
|
||||
{
|
||||
"name": "getComponentViewByIndex"
|
||||
"name": "getComponentLViewByIndex"
|
||||
},
|
||||
{
|
||||
"name": "getComponentViewByInstance"
|
||||
|
|
Loading…
Reference in New Issue