fix(ivy): ViewRef.rootNodes not including projected nodes (#28951)
Currently if an embedded view contains projected nodes, its `rootNodes` array will include `null` instead of the root nodes inside the projection slot. This manifested itself in one of the Material unit tests where we stamp out a template and then move its `rootNodes` into the overlay container. This PR is related to FW-1087. PR Close #28951
This commit is contained in:
parent
dbd9ecfd4c
commit
25a2fef303
|
@ -12,10 +12,10 @@ import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_co
|
||||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
||||||
|
|
||||||
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions';
|
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions';
|
||||||
import {TNode, TNodeType, TViewNode} from './interfaces/node';
|
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
|
||||||
import {FLAGS, HOST, LView, LViewFlags, PARENT, T_HOST} from './interfaces/view';
|
import {FLAGS, HOST, LView, LViewFlags, T_HOST} from './interfaces/view';
|
||||||
import {destroyLView} from './node_manipulation';
|
import {destroyLView} from './node_manipulation';
|
||||||
import {getLViewParent} from './util/view_traversal_utils';
|
import {findComponentView, getLViewParent} from './util/view_traversal_utils';
|
||||||
import {getNativeByTNode} from './util/view_utils';
|
import {getNativeByTNode} from './util/view_utils';
|
||||||
|
|
||||||
|
|
||||||
|
@ -291,9 +291,21 @@ function collectNativeNodes(lView: LView, parentTNode: TNode, result: any[]): an
|
||||||
let tNodeChild = parentTNode.child;
|
let tNodeChild = parentTNode.child;
|
||||||
|
|
||||||
while (tNodeChild) {
|
while (tNodeChild) {
|
||||||
result.push(getNativeByTNode(tNodeChild, lView));
|
const nativeNode = getNativeByTNode(tNodeChild, lView);
|
||||||
|
nativeNode && result.push(nativeNode);
|
||||||
if (tNodeChild.type === TNodeType.ElementContainer) {
|
if (tNodeChild.type === TNodeType.ElementContainer) {
|
||||||
collectNativeNodes(lView, tNodeChild, result);
|
collectNativeNodes(lView, tNodeChild, result);
|
||||||
|
} else if (tNodeChild.type === TNodeType.Projection) {
|
||||||
|
const componentView = findComponentView(lView);
|
||||||
|
const componentHost = componentView[T_HOST] as TElementNode;
|
||||||
|
const parentView = getLViewParent(componentView);
|
||||||
|
let currentProjectedNode: TNode|null =
|
||||||
|
(componentHost.projection as(TNode | null)[])[tNodeChild.projection as number];
|
||||||
|
|
||||||
|
while (currentProjectedNode && parentView) {
|
||||||
|
result.push(getNativeByTNode(currentProjectedNode, parentView));
|
||||||
|
currentProjectedNode = currentProjectedNode.next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tNodeChild = tNodeChild.next;
|
tNodeChild = tNodeChild.next;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* @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 {Component, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
|
||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
|
||||||
|
describe('TemplateRef', () => {
|
||||||
|
describe('rootNodes', () => {
|
||||||
|
it('should include projected nodes in rootNodes', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'menu-content',
|
||||||
|
template: `
|
||||||
|
<ng-template>
|
||||||
|
Header
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</ng-template>
|
||||||
|
`,
|
||||||
|
exportAs: 'menuContent'
|
||||||
|
})
|
||||||
|
class MenuContent {
|
||||||
|
@ViewChild(TemplateRef) template !: TemplateRef<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<menu-content #menu="menuContent">
|
||||||
|
<button>Item one</button>
|
||||||
|
<button>Item two</button>
|
||||||
|
</menu-content>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
@ViewChild(MenuContent) content !: MenuContent;
|
||||||
|
|
||||||
|
constructor(public viewContainerRef: ViewContainerRef) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [MenuContent, App]});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const instance = fixture.componentInstance;
|
||||||
|
const viewRef = instance.viewContainerRef.createEmbeddedView(instance.content.template);
|
||||||
|
const rootNodeTextContent = viewRef.rootNodes.map(node => node && node.textContent.trim())
|
||||||
|
.filter(text => text !== '');
|
||||||
|
|
||||||
|
expect(rootNodeTextContent).toEqual(['Header', 'Item one', 'Item two']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -685,10 +685,6 @@ window.testBlocklist = {
|
||||||
"error": "Error: Expected null to be 'mat-dialog-title-12', 'Expected the aria-labelledby to match the title id.'.",
|
"error": "Error: Expected null to be 'mat-dialog-title-12', 'Expected the aria-labelledby to match the title id.'.",
|
||||||
"notes": "FW-1097: Static host classes and styles don't work on root component"
|
"notes": "FW-1097: Static host classes and styles don't work on root component"
|
||||||
},
|
},
|
||||||
"MatMenu should open a custom menu": {
|
|
||||||
"error": "Error: Expected function not to throw an Error, but it threw TypeError.",
|
|
||||||
"notes": "Unknown"
|
|
||||||
},
|
|
||||||
"MatMenu should close the menu when using the CloseScrollStrategy": {
|
"MatMenu should close the menu when using the CloseScrollStrategy": {
|
||||||
"error": "TypeError: Cannot read property 'openMenu' of undefined",
|
"error": "TypeError: Cannot read property 'openMenu' of undefined",
|
||||||
"notes": "Unknown"
|
"notes": "Unknown"
|
||||||
|
|
Loading…
Reference in New Issue