简体   繁体   English

如何访问 Angular2 指令上的组件?

[英]How to access the Component on a Angular2 Directive?

I'm doing some tests with Angular 2 and I have a directive (layout-item) that can be applied to all my components.我正在用 Angular 2 做一些测试,我有一个指令(布局项)可以应用于我的所有组件。

Inside that directive I want to be able to read some metadata defined on the component but for that I need to access the component's reference.在该指令中,我希望能够读取组件上定义的一些元数据,但为此我需要访问组件的引用。

I have tried the following approach but I was unable to get what I need.我尝试了以下方法,但无法获得所需的内容。 Does any one has a suggestion?有没有人有建议?

@Component({...})
@View({...})
@MyAnnotation({...})
export class MyComponentA {...}


// Somewhere in a template
<myComponentA layout-item="my config 1"></myComponentA>
<myComponentB layout-item="my config 2"></myComponentA>

// ----------------------

@ng.Directive({
    selector: "[layout-item]",
    properties: [
        "strOptions: layout-item"
    ],
    host: {

    }
})

export class LayoutItem {

    // What works
    constructor(@Optional() @Ancestor({self: true}) private component: MyComponent1) {

 // with the constructor defined like this, component is defined with myComponent1 instance.
Reflector.getMetadata("MyAnnotation", component.constructor); // > metadata is here!
    }

// What I needed
    constructor(@Optional() @Ancestor({self: true}) private component: any) {

 // This will crash the app. If instead of any I specify some other type, the app will not crash but component will be null.
 // This directive can be applied to any component, so specifying a type is not a solution. 
    }
}

UPDATE :更新

Since Beta 16 there is no official way to get the same behavior.从 Beta 16 开始,没有正式的方法来获得相同的行为。 There is an unofficial workaround here: https://github.com/angular/angular/issues/8277#issuecomment-216206046这里有一个非官方的解决方法: https : //github.com/angular/angular/issues/8277#issuecomment-216206046


Thanks @Eric Martinez, your pointers were crucial in getting me in the right direction!谢谢@Eric Martinez,您的指点对于让我朝着正确的方向发展至关重要!

So, taking Eric's approach, I have managed to do the following:因此,采用 Eric 的方法,我设法做到了以下几点:

HTML HTML

<my-component layout-item="my first component config"></my-component>

<my-second-component layout-item="my second component config"></my-second-component>

<my-third-component layout-item="my third component config"></my-third-component>

Three different components, all of the share the same layout-item attribute.三个不同的组件,都共享相同的layout-item属性。

Directive指示

@Directive({
  selector : '[layout-item]'
})
export class MyDirective {
  constructor(private _element: ElementRef, private _viewManager: AppViewManager) {
    let hostComponent = this._viewManager.getComponent(this._element);
    // on hostComponent we have our component! (my-component, my-second-component, my-third-component, ... and so on!
  }

}

Forget about the Service, there's a simpler form of doing this忘记服务,有一种更简单的形式

Option 1 (Not what you need, but it may be useful for other users)选项 1 (不是您需要的,但它可能对其他用户有用)

HTML HTML

<my-component layout-item="my first component config"></my-component>

<my-second-component layout-item="my second component config"></my-second-component>

<my-third-component layout-item="my third component config"></my-third-component>

Three different components, all of the share the same layout-item property.三个不同的组件,都共享相同的layout-item属性。

Directive指示

@Directive({
  selector : '[layout-item]',
  properties: ['myParentConfig: my-parent-config'] // See the components for this property
})
export class MyDirective {
  constructor() {

  }

  onInit() {
    console.log(this.myParentConfig);
  }
}

Pretty straightforward, not much to explain here很简单,这里就不多解释了

Component成分

@Component({
  selector : 'my-component',
  properties : ['myConfig: layout-item']
})
@View({
  template : `<div [my-parent-config]="myConfig" layout-item="my config"></div>`,
  directives : [MyDirective]
})
export class MyComponent {
  constructor() {
  }
}

I'm pretty sure that you understand this, but for the sake of a good answer I will explain what it does我很确定你明白这一点,但为了一个好的答案,我将解释它的作用

properties : ['myConfig: layout-item']`

This line assigns the layout-item property to the internal myConfig property.此行将layout-item属性分配给内部myConfig属性。

Component's template组件的模板

template : `<div [my-parent-config]="myConfig" layout-item="my config"></div>`,

We are creating a my-parent-config property for the directive and we assign the parent's config to it.我们正在为指令创建一个my-parent-config属性,并将父级的配置分配给它。

As simple as that!就如此容易! So now we can add more components with (pretty much) the same code所以现在我们可以使用(几乎)相同的代码添加更多组件

Second component第二个组件

@Component({
  selector : 'my-second-component',
  properties : ['myConfig: layout-item']
})
@View({
  template : `<div [my-parent-config]="myConfig" layout-item="my config"></div>`,
  directives : [MyDirective]
})
export class MySecondComponent {
  constructor() {
  }
}  

See?看? Was much easier than my idea of using services (awful but 'working' idea).比我使用服务的想法要容易得多(可怕但“有效”的想法)。

With this way it is much simpler and cleaner.通过这种方式,它更简单、更干净。 Here's the plnkr so you can test it.这是plnkr,因此您可以对其进行测试。

(It wasn't what you need :'( ) (这不是你需要的:'()

UPDATE更新

Option 2选项 2

For what I understood of your updated question is that you need a reference to the component, so what I came up with is pretty similar to my original answer我对你更新的问题的理解是你需要对组件的引用,所以我想出的与我原来的答案非常相似

What I did :我做了什么 :

  • First I made the components to hold a reference to themselves首先,我使组件保持对自身的引用
<my-cmp-a #pa [ref]="pa" layout-item="my first component config"></my-cmp-a>
<my-cmp-b #pb [ref]="pb" layout-item="my first component config"></my-cmp-b>
<my-cmp-c #pc [ref]="pc" layout-item="my first component config"></my-cmp-c>
  • Then I passed each reference to the LayoutItem directive (which was injected in each component, not at top-level)然后我将每个引用传递给LayoutItem指令(它被注入到每个组件中,而不是在顶级)
@Component({
  selector : 'my-cmp-a',
  properties : ['ref: ref']
})
@View({
  template : '<div [parent-reference]="ref" layout-item=""></div>',
  directives : [LayoutItem]
})
@YourCustomAnnotation({})
export class MyCmpA {
  constructor() {

  }
}
  • Finally in the directive you can have access to the component's constructor (from your updated question I guess that's all you need to get its metadata) (You must use it inside onInit, "reference" won't exist in constructor)最后在指令中,您可以访问组件的构造函数(从您更新的问题我猜这就是您获取其元数据所需的全部内容) (您必须在 onInit 中使用它,构造函数中不存在“引用”)
@Directive({
  selector : '[layout-item]',
  properties : ['reference: parent-reference']
})
export class LayoutItem {
  constructor() {
  }

  onInit() {
    console.log(this.reference.constructor);
    Reflector.getMetadata("YourCustomAnnotation", this.reference.constructor);
  }
}

Use this plnkr to do your tests.使用此plnkr进行测试。

I was able to get access to a directive's host component by asking the injector for it.我能够通过向注入器询问指令的主机组件来访问它。

@Directive({
  selector: '[with-foo]'
})
export class WithFooDirective implements OnInit {
  constructor(private myComponent: MyComponent) { }

  ngOnInit() {
    console.debug(this.myComponent.foo()) // > bar
  }
}

@Component({
  selector: 'my-component',
  template: '<div></div>'
})
export class MyComponent {
  public foo() { return 'bar' }
}

... ...

<my-component with-foo></my-component>

It seems that most convenient and clean way is to use provider alias:似乎最方便和干净的方法是使用提供者别名:

//ParentComponent declaration
providers: [{ provide: Parent, useExisting: forwardRef(() => ParentComponent) }]

where Parent is separate class that works as OpaqueToken and abstract class at the same type.其中Parent是单独的类,作为OpaqueToken和相同类型的抽象类。

//directive
constructor(@Optional() @Host() parent:Parent) {}

Each component that is accessed from child directive should provide itself.从子指令访问的每个组件都应提供自身。

This is described in documentation: link这在文档中有所描述: 链接

This solution was linked to in the comments of one of the other answers but it was hidden at the end of quite a long discussion so I will add it here.该解决方案已链接到其他答案之一的评论中,但它隐藏在相当长的讨论结束时,因此我将在此处添加它。

Import ViewContainerRef and inject it into your directive.导入 ViewContainerRef 并将其注入您的指令中。

import { ViewContainerRef } from '@angular/core';
...
constructor(private viewContainerRef: ViewContainerRef) {}

You can then access the following private/unsupported property path to retrieve the component instance which is associated with the element that has been decorated with the directive.然后,您可以访问以下私有/不受支持的属性路径以检索与已使用指令修饰的元素关联的组件实例。

this.viewContainerRef._data.componentView.component

Not the most convenient, but reliable method.不是最方便但可靠的方法。 Directive:指示:

@Input() component: any;

Component:成分:

[component]="this"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM