Angular - child 组件可以引用 parent 的模板变量吗?

Angular - Can a child component reference a parent's template variable?

我正在使用 primefaces primeng 组件开发 angular 4 应用程序,p-contextmenu。我试图告诉 child 元素使用 parent 组件的模板变量。

app.html:

<div>
  <router-outlet></router-outlet>
  <div #contextMenuHolder></div>
</div>

mycomponent.html:

<p-contextMenu [appendTo]="contextMenuHolder" [model]="items"></p-contextMenu>

显然它失败了,因为 contextMenuHolder 在 child 中不存在,但在它的 parent 中:

Angular: Identifier 'contextMenuHolder' is not defined. The component declaration, template variable declarations, and element references do not contain such a member

你能从 child 组件引用 parent 的模板变量吗?

编辑:

Plunkr with it broken。这个 plunkr 显示它不工作,但没有错误消息。

appendTo 的文档说

Target element to attach the overlay, valid values are "body" or a local ng-template variable of another element.

也许服务可以解决问题:

@Injectable()
export class ContextMenuHolder {
  contextMenu: any; // TODO set a type (HTMLElement?)

  getContextMenu() {
    return this.contextMenu;
  }

  setContextMenu(contextMenu: any) {
    this.contextMenu = contextMenu;
  }
}

在您的 app.ts 中,您注入服务并设置值。 在您的 component.ts 中,您注入服务并获取值。

我没有测试它,但它应该 工作。如果 contextMenu 可以更改,您将不得不使用事件侦听器或可观察的。

感谢 Ludovic Guillaume,我找到了解决方案:

https://plnkr.co/edit/kwnkSKDPFs1Bp2xOHqIu

child.ts:

import {Component, NgModule, VERSION} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {ContextMenuHolderService} from './context-menu-holder.service'

@Component({
  selector: 'child-comp',
  template: `
    <div>
      <h2>Hello child</h2>
      <span #mySpan>this bit has a context menu</span> <br>
      <span #parentSpan>this bit is the target for the parent span.</span>

      <p-contextMenu [target]="mySpan" [appendTo]="parentContext" [model]="items"></p-contextMenu>
      <p-contextMenu [target]="parentSpan" [appendTo]="parentContext" [model]="itemsForParent"></p-contextMenu>
    </div>
  `,
})

export class ChildComponent {
  private items: MenuItem[];
  parentContext: any;

   constructor(private cmhs : ContextMenuHolderService) {

    }

    ngOnInit() {
      this.items = [{ label: 'mySpans context menu' }];
      this.itemsForParent =  [{ label: 'parent context menu items' }];
      console.log('child init', this.cmhs.getContextMenuParent())
      this.parentContext = this.cmhs.getContextMenuParent().nativeElement;
    }
}

此处,child 组件已构建上下文菜单,其中包含菜单中所需的项目。此菜单需要驻留在 parent 中(有时这是出于样式或定位原因所必需的)。 child 有一个 parentContext object 将在生命周期的 onInit 阶段设置。

parent (app.ts):

//our root app component
import {Component, NgModule, VERSION, ViewChild} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {ChildComponent} from './child'
import {ContextMenuModule,MenuItem} from 'primeng/primeng'
import {ContextMenuHolderService} from './context-menu-holder.service'

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <div #parentContextTarget>This is in the parent component and should have a context menu</div>
      <div #parentContextWrapper></div>
      <child-comp></child-comp>
    </div>
  `,
})


export class App {
  name:string;
  @ViewChild('parentContextWrapper') parentContextWrapper;

  constructor(private cmhs : ContextMenuHolderService) {
    this.name = `Angular! v${VERSION.full}`
    // console.log('parent constructor')
  }

  ngOnInit(){
    console.log('parent init - parent context wrapper', this.parentContextWrapper)
    this.cmhs.setContextMenuParent(this.parentContextWrapper)
  }


}

parent 在 onInit 阶段在服务中设置 object。最初我认为这必须在 afterViewInit 期间进行,但结果在生命周期中为时已晚。

服务:

import {Injectable} from '@angular/core';

@Injectable()
export class ContextMenuHolderService {
  contextMenuParent: any; // TODO set a type (HTMLElement?)

  getContextMenuParent() {
    console.log('returning cmp', this.contextMenuParent)
    return this.contextMenuParent;
  }

  setContextMenuParent(contextMenuParent: any) {
    console.log('settin context menu parent', contextMenuParent)
    this.contextMenuParent = contextMenuParent;
  }
}