【问题标题】:Angular - Can a child component reference a parent's template variable?Angular - 子组件可以引用父模板变量吗?
【发布时间】:2017-08-17 22:03:02
【问题描述】:

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

app.html:

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

我的组件.html:

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

显然它失败了,因为 contextMenuHolder 不存在于子元素中,但存在于其父元素中:

Angular:未定义标识符“contextMenuHolder”。组件声明、模板变量声明和元素引用不包含这样的成员

你能从子组件中引用父模板变量吗?

编辑:

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

【问题讨论】:

  • 请更正您的标签...阅读他们的信息

标签: angular primeng


【解决方案1】:

appendTo 的文档说

附加叠加层的目标元素,有效值为“body”或另一个元素的本地 ng-template 变量。

也许服务可以解决问题:

@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 可以更改,则必须使用事件侦听器或 observable。

【讨论】:

  • 这给了我这个错误:Cannot append [object Object] to [object HTMLDivElement]
  • @Jeff 我搞砸了你之前提到的Cannot append [object Object] to [object HTMLDivElement]。出于某种原因,当我从 app-root-component 设置服务 &lt;div #holder&gt;&lt;/div&gt; 时,一切正常。但是,当我从根组件的任何嵌套中使用 holder 时,就会发生此错误。您对摆脱错误的方法有什么想法吗?
  • 不是我的头,但发布另一个问题并链接它。
【解决方案2】:

感谢 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;
    }
}

在这里,子组件构建了上下文菜单,其中包含它想要的菜单项。此菜单需要驻留在父级中(有时出于样式或定位原因,这是必需的)。孩子有一个 parentContext 对象,该对象将在其生命周期的 onInit 阶段设置。

父级(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)
  }


}

父级在onInit 阶段将对象设置在服务中。最初我认为这必须是在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;
  }
}

【讨论】:

  • 我看到的唯一奇怪的问题...两个上下文菜单可以同时打开。
  • 谢谢!如果有人找到此答案并且遇到错误“无法将 [object Object] 附加到 [object HTMLDivElement]”的问题,则解决方案是使用 .nativeElement,如此答案中所述。
  • @jgosar 创建一个新问题。我很乐意看看。
  • 这不是一个新问题,我只是想指出,Google 搜索“无法将 [object Object] 附加到 [object HTMLDivElement]”将我带到这里,并且解决方案已经在您的回答,如果你知道在哪里看:)
猜你喜欢
  • 1970-01-01
  • 2017-11-04
  • 1970-01-01
  • 1970-01-01
  • 2019-03-12
  • 2018-07-14
  • 2017-04-27
  • 1970-01-01
  • 2011-01-14
相关资源
最近更新 更多