如何从指令访问主机组件?

Ang*_*hef 26 angular-directive angular

说我有以下标记:

<my-comp myDirective></my-comp>
Run Code Online (Sandbox Code Playgroud)

有没有办法可以从指令访问组件实例?

更具体地说,我希望能够访问MyComponentfrom 的属性和方法MyDirective,理想情况不向上面的HTML添加任何内容.

Gün*_*uer 29

你可以注射它

class MyDirective {
  constructor(private host:MyComponent) {}
Run Code Online (Sandbox Code Playgroud)

严重的限制是,您需要事先知道组件的类型.

另请参阅https://github.com/angular/angular/issues/8277
它还提供了一些解决方法,以便您提前知道类型.


Mic*_*dey 18

如果您想在自定义组件上使用属性指令,您可以让这些组件从抽象类扩展,并将抽象类类型“forwardRef”扩展到您的组件类型。通过这种方式,您可以在抽象类(在您的指令内)上选择 angular 的 DI。

抽象类:

export abstract class MyReference { 
  // can be empty if you only want to use it as a reference for DI
}
Run Code Online (Sandbox Code Playgroud)

自定义组件:

@Component({
  // ...
  providers: [
    {provide: MyReference, useExisting: forwardRef(() => MyCustomComponent)}
  ],
})
export class MyCustomComponent extends MyReference implements OnInit {
// ...
}
Run Code Online (Sandbox Code Playgroud)

指示:

@Directive({
  selector: '[appMyDirective]'
})
export class CustomDirective{

  constructor(private host:MyReference) {
    console.log(this.host);
    // no accessing private properties of viewContainerRef to see here... :-)
  }

}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以在扩展抽象类的任何组件上使用该指令。

这当然只适用于您自己的组件。


Sun*_*arg 11

您的指令可能是通用指令,可以应用于您的任何组件。因此,在那种情况下,将无法在构造函数中注入组件,因此这是另一种方法

注入ViewContainerRef in构造函数

constructor(private _viewContainerRef: ViewContainerRef) { }
Run Code Online (Sandbox Code Playgroud)

然后使用

let hostComponent = this._viewContainerRef["_data"].componentView.component;
Run Code Online (Sandbox Code Playgroud)

  • 从 Angular 9.0.0-next.3 开始,此解决方案不再有效。你知道他们现在可能把它藏在哪里了吗? (8认同)
  • @Fredrik_Macrobond _.last(this.viewContainerRef['_hostView'][0].__ngContext__) (2认同)
  • Angular 12 的一个非常肮脏的解决方案:`(this.viewContainerRef as any)._hostLView[8]`。8 参考https://github.com/angular/angular/blob/a92a89b0eb127a59d7e071502b5850e57618ec2d/packages/core/src/render3/interfaces/view.ts#L180中的CONTEXT(内联)要谨慎使用。 (2认同)

Ant*_*ony 7

这取自github问题,就像一个魅力.缺点是需要事先知道组件,但在您的情况下,您需要知道您正在使用的方法.

import { Host, Self, Optional } from '@angular/core';

    constructor(
         @Host() @Self() @Optional() public hostCheckboxComponent : MdlCheckboxComponent
        ,@Host() @Self() @Optional() public hostSliderComponent   : MdlSliderComponent){
                if(this.hostCheckboxComponent) {
                       console.log("host is a checkbox");
                } else if(this.hostSliderComponent) {
                       console.log("host is a slider");
                }
         }
Run Code Online (Sandbox Code Playgroud)

图片来源:https: //github.com/angular/angular/issues/8277#issuecomment-323678013


AC1*_*101 6

使指令通用的一个可能的解决方法是将组件的模板引用作为 @Input 传递给指令。这增加了一些额外的 html,但它比我尝试过的许多其他技巧效果更好。

@Directive({selector: '[myDirective]'})
export class MyDirective implements OnInit {
  @Input() componentRef: any;
  @Input() propName: string;

  ngOnInit(){
    if (this.componentRef != null) {
      // Access component properties
      this.componentRef[this.propName];
    }
 }
}
Run Code Online (Sandbox Code Playgroud)

视图中的用法:

@Directive({selector: '[myDirective]'})
export class MyDirective implements OnInit {
  @Input() componentRef: any;
  @Input() propName: string;

  ngOnInit(){
    if (this.componentRef != null) {
      // Access component properties
      this.componentRef[this.propName];
    }
 }
}
Run Code Online (Sandbox Code Playgroud)

也适用于其他指令。使用装饰器的exportAs属性@Directive来获取指令实例的引用。

<!-- Pass component ref and the property name from component as inputs -->
<app-component #appComponentRef myDirective [componentRef]="appComponentRef" [propName]="'somePropInComponent'" .... >
Run Code Online (Sandbox Code Playgroud)