diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts
index a25468d911..95539e1b5b 100644
--- a/packages/core/src/render3/view_ref.ts
+++ b/packages/core/src/render3/view_ref.ts
@@ -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 {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions';
-import {TNode, TNodeType, TViewNode} from './interfaces/node';
-import {FLAGS, HOST, LView, LViewFlags, PARENT, T_HOST} from './interfaces/view';
+import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
+import {FLAGS, HOST, LView, LViewFlags, T_HOST} from './interfaces/view';
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';
@@ -291,9 +291,21 @@ function collectNativeNodes(lView: LView, parentTNode: TNode, result: any[]): an
let tNodeChild = parentTNode.child;
while (tNodeChild) {
- result.push(getNativeByTNode(tNodeChild, lView));
+ const nativeNode = getNativeByTNode(tNodeChild, lView);
+ nativeNode && result.push(nativeNode);
if (tNodeChild.type === TNodeType.ElementContainer) {
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;
}
diff --git a/packages/core/test/acceptance/template_ref_spec.ts b/packages/core/test/acceptance/template_ref_spec.ts
new file mode 100644
index 0000000000..981cc43bac
--- /dev/null
+++ b/packages/core/test/acceptance/template_ref_spec.ts
@@ -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: `
+
+ Header
+
+
+ `,
+ exportAs: 'menuContent'
+ })
+ class MenuContent {
+ @ViewChild(TemplateRef) template !: TemplateRef;
+ }
+
+ @Component({
+ template: `
+
+
+
+
+ `
+ })
+ 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']);
+ });
+ });
+
+});
diff --git a/tools/material-ci/angular_material_test_blocklist.js b/tools/material-ci/angular_material_test_blocklist.js
index 092c0a5e3c..e924cf2ff4 100644
--- a/tools/material-ci/angular_material_test_blocklist.js
+++ b/tools/material-ci/angular_material_test_blocklist.js
@@ -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.'.",
"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": {
"error": "TypeError: Cannot read property 'openMenu' of undefined",
"notes": "Unknown"