我正在使用Angular 2进行一些测试,并且我有一个可以应用于我所有组件的指令(layout-item)。
在该指令中,我希望能够读取组件上定义的一些元数据,但为此我需要访问该组件的参考。
我尝试了以下方法,但我无法得到我需要的东西。有人有建议吗?
@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.
}
}
答案 0 :(得分:9)
<强>更新强>:
自Beta 16以来,没有官方方法可以获得相同的行为。这里有一个非官方的解决方法:https://github.com/angular/angular/issues/8277#issuecomment-216206046
感谢@Eric Martinez,你的指示对于让我走向正确的方向至关重要!
所以,采用Eric的方法,我设法做到了以下几点:
<强> 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>
三个不同的组件,所有组件共享相同的layout-item
属性。
<强>指令强>
@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!
}
}
答案 1 :(得分:5)
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)
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.
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.
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.
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.
(It wasn't what you need :'( )
UPDATE
Option 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 :
<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>
LayoutItem
directive (which was injected in each component, not at top-level)@Component({
selector : 'my-cmp-a',
properties : ['ref: ref']
})
@View({
template : '<div [parent-reference]="ref" layout-item=""></div>',
directives : [LayoutItem]
})
@YourCustomAnnotation({})
export class MyCmpA {
constructor() {
}
}
@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.
答案 2 :(得分:3)
我可以通过向注射器询问它来访问指令的主机组件。
@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>
答案 3 :(得分:1)
似乎最方便和最干净的方法是使用提供程序别名:
//ParentComponent declaration
providers: [{ provide: Parent, useExisting: forwardRef(() => ParentComponent) }]
其中Parent
是作为OpaqueToken
的单独类和相同类型的抽象类。
//directive
constructor(@Optional() @Host() parent:Parent) {}
从child指令访问的每个组件都应该自行提供。
文档中描述了这一点:link
答案 4 :(得分:0)
这个解决方案与其他一个答案的评论相关联,但它隐藏在相当long discussion的末尾,所以我将在此处添加。
导入ViewContainerRef并将其注入您的指令。
import { ViewContainerRef } from '@angular/core';
...
constructor(private viewContainerRef: ViewContainerRef) {}
然后,您可以访问以下私有/不受支持的属性路径,以检索与使用该指令修饰的元素关联的组件实例。
this.viewContainerRef._data.componentView.component
答案 5 :(得分:0)
不是最方便但可靠的方法。 指令:
@Input() component: any;
组件:
[component]="this"