如何访问 Angular2 指令上的组件?
How to access the Component on a Angular2 Directive?
我正在用 Angular 2 做一些测试,我有一个指令(布局项)可以应用于我的所有组件。
在该指令中,我希望能够读取组件上定义的一些元数据,但为此我需要访问组件的引用。
我已经尝试了以下方法,但我无法得到我需要的东西。有人有什么建议吗?
@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.
}
}
忘掉服务吧,有一种更简单的方式可以做到这一点
选项 1
(不是你需要的,但可能对其他用户有用)
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]',
properties: ['myParentConfig: my-parent-config'] // See the components for this property
})
export class MyDirective {
constructor() {
}
onInit() {
console.log(this.myParentConfig);
}
}
很简单,这里就不多解释了
组件
@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() {
}
}
我很确定你明白这一点,但为了一个好的答案,我将解释它的作用
properties : ['myConfig: layout-item']`
此行将 layout-item
属性 分配给内部 myConfig
属性.
组件模板
template : `<div [my-parent-config]="myConfig" layout-item="my config"></div>`,
我们正在为指令创建一个 my-parent-config
属性 并将父级的配置分配给它。
就这么简单!所以现在我们可以使用(几乎)相同的代码添加更多组件
第二个分量
@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() {
}
}
看到了吗?比我 using services 的想法容易得多(糟糕但 'working' 的想法)。
通过这种方式,它更加简单和干净。这是 plnkr,您可以对其进行测试。
(这不是你需要的 :'( )
更新
选项 2
据我了解,您更新后的问题是您需要对组件的引用,因此我得出的结论与我原来的答案非常相似
我做了什么:
- 首先,我让组件持有对自身的引用
<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
指令(它被注入到每个组件中,而不是在顶层)
@Component({
selector : 'my-cmp-a',
properties : ['ref: ref']
})
@View({
template : '<div [parent-reference]="ref" layout-item=""></div>',
directives : [LayoutItem]
})
@YourCustomAnnotation({})
export class MyCmpA {
constructor() {
}
}
- 最后,在指令中,您可以访问组件的构造函数(根据您更新的问题,我想这就是获取其元数据所需的全部内容)
(必须在onInit中使用,"reference"不会存在于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);
}
}
使用此 plnkr 进行测试。
更新:
自 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!
}
}
看来最方便干净的方法是使用提供商别名:
//ParentComponent declaration
providers: [{ provide: Parent, useExisting: forwardRef(() => ParentComponent) }]
其中 Parent
是独立的 class,它作为 OpaqueToken
和抽象 class 在同一类型下工作。
//directive
constructor(@Optional() @Host() parent:Parent) {}
从子指令访问的每个组件都应提供自身。
这在文档中有描述:link
此解决方案已链接到其他答案之一的评论中,但它隐藏在相当多的 long discussion 末尾,因此我将其添加到此处。
导入 ViewContainerRef 并将其注入您的指令。
import { ViewContainerRef } from '@angular/core';
...
constructor(private viewContainerRef: ViewContainerRef) {}
然后您可以访问以下 private/unsupported 属性 路径来检索与已使用指令修饰的元素关联的组件实例。
this.viewContainerRef._data.componentView.component
我能够通过向注入器请求访问指令的主机组件。
@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>
不是最方便但可靠的方法。
指令:
@Input() component: any;
组件:
[component]="this"
我正在用 Angular 2 做一些测试,我有一个指令(布局项)可以应用于我的所有组件。
在该指令中,我希望能够读取组件上定义的一些元数据,但为此我需要访问组件的引用。
我已经尝试了以下方法,但我无法得到我需要的东西。有人有什么建议吗?
@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.
}
}
忘掉服务吧,有一种更简单的方式可以做到这一点
选项 1 (不是你需要的,但可能对其他用户有用)
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]',
properties: ['myParentConfig: my-parent-config'] // See the components for this property
})
export class MyDirective {
constructor() {
}
onInit() {
console.log(this.myParentConfig);
}
}
很简单,这里就不多解释了
组件
@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() {
}
}
我很确定你明白这一点,但为了一个好的答案,我将解释它的作用
properties : ['myConfig: layout-item']`
此行将 layout-item
属性 分配给内部 myConfig
属性.
组件模板
template : `<div [my-parent-config]="myConfig" layout-item="my config"></div>`,
我们正在为指令创建一个 my-parent-config
属性 并将父级的配置分配给它。
就这么简单!所以现在我们可以使用(几乎)相同的代码添加更多组件
第二个分量
@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() {
}
}
看到了吗?比我 using services 的想法容易得多(糟糕但 'working' 的想法)。
通过这种方式,它更加简单和干净。这是 plnkr,您可以对其进行测试。
(这不是你需要的 :'( )
更新
选项 2
据我了解,您更新后的问题是您需要对组件的引用,因此我得出的结论与我原来的答案非常相似
我做了什么:
- 首先,我让组件持有对自身的引用
<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
指令(它被注入到每个组件中,而不是在顶层)
@Component({
selector : 'my-cmp-a',
properties : ['ref: ref']
})
@View({
template : '<div [parent-reference]="ref" layout-item=""></div>',
directives : [LayoutItem]
})
@YourCustomAnnotation({})
export class MyCmpA {
constructor() {
}
}
- 最后,在指令中,您可以访问组件的构造函数(根据您更新的问题,我想这就是获取其元数据所需的全部内容) (必须在onInit中使用,"reference"不会存在于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);
}
}
使用此 plnkr 进行测试。
更新:
自 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!
}
}
看来最方便干净的方法是使用提供商别名:
//ParentComponent declaration
providers: [{ provide: Parent, useExisting: forwardRef(() => ParentComponent) }]
其中 Parent
是独立的 class,它作为 OpaqueToken
和抽象 class 在同一类型下工作。
//directive
constructor(@Optional() @Host() parent:Parent) {}
从子指令访问的每个组件都应提供自身。
这在文档中有描述:link
此解决方案已链接到其他答案之一的评论中,但它隐藏在相当多的 long discussion 末尾,因此我将其添加到此处。
导入 ViewContainerRef 并将其注入您的指令。
import { ViewContainerRef } from '@angular/core';
...
constructor(private viewContainerRef: ViewContainerRef) {}
然后您可以访问以下 private/unsupported 属性 路径来检索与已使用指令修饰的元素关联的组件实例。
this.viewContainerRef._data.componentView.component
我能够通过向注入器请求访问指令的主机组件。
@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>
不是最方便但可靠的方法。 指令:
@Input() component: any;
组件:
[component]="this"