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

时间:2017-09-02 14:25:09

标签: angular angular-directive

说我有以下标记:

<my-comp myDirective></my-comp>

我有什么方法可以从指令访问组件实例

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

9 个答案:

答案 0 :(得分:24)

你可以注射它

class MyDirective {
  constructor(private host:MyComponent) {}

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

另见https://github.com/angular/angular/issues/8277
它还为您提前知道类型提供了一些解决方法。

答案 1 :(得分:8)

您的指令可以是可应用于任何组件的通用指令。所以,在这种情况下,在构造函数中注入组件是不可能的,所以这是另一种方法来做同样的事情

在构造函数中注入ViewContainerRef

constructor(private _viewContainerRef: ViewContainerRef) { }

然后使用

获取它
let hostComponent = this._viewContainerRef["_data"].componentView.component;

答案 2 :(得分:6)

这取自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");
                }
         }

信用: https://github.com/angular/angular/issues/8277#issuecomment-323678013

答案 3 :(得分:1)

如果要在自定义组件上使用attribute指令,则可以使这些组件从抽象类扩展,并将'forwardRef'抽象类类型扩展为您的组件类型。这样,您可以在抽象类中(在您的指令内)选择angular的DI。

抽象类:

export abstract class MyReference { 
  // can be empty if you only want to use it as a reference for DI
}

自定义组件:

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

指令:

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

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

}

这样,您可以在扩展抽象类的任何组件上使用指令。

这当然只会在您自己的组件上起作用。

答案 4 :(得分:1)

您可以使用 ViewContainerRef 访问宿主组件。

constructor(private el: ViewContainerRef) {}

ngOnInit() {
    const _component = this.el && this.el.injector && this.el.injector.get(MyComponent);
}

参考:https://angular.io/api/core/ViewContainerRef

答案 5 :(得分:1)

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

@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];
    }
 }
}

视图中的用法:

<!-- Pass component ref and the property name from component as inputs -->
<app-component #appComponentRef myDirective [componentRef]="appComponentRef" [propName]="'somePropInComponent'" .... >

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

<form #myForm="ngForm" myDirective [componentRef]="myForm" [propName]="'ngSubmit'" ....>

答案 6 :(得分:0)

我喜欢这样,它可以在Angular 9上使用。

export class FromItemComponentBase  {
  constructor(private hostElement: ElementRef) {
    hostElement.nativeElement.__component=this;
  }
}

@Component({
  selector: 'input-error',
  templateUrl: 'component.html'
})
export class FromItemErrorComponent extends FromItemComponentBase {
  constructor(private hostElement: ElementRef) {
    super(hostElement);
  }
}

@Component({
  selector: 'input-password',
  templateUrl: 'component.html'
})
export class FromItemPasswordComponent extends FromItemComponentBase {
  constructor(private hostElement: ElementRef) {
    super(hostElement);
  }
}

@Directive({selector: 'input-error,input-password,input-text'})
export class FormInputDirective {
  component:FromItemComponentBase;

  constructor(private hostElement: ElementRef) {
    this.component=hostElement.nativeElement.__component;
  }
}

答案 7 :(得分:0)

注意:这很笨拙,可能在 Angular 的未来版本中不起作用。 在 Angular 10 中,我能够像这样访问主机组件:

与@Sunil Garg 的解决方案类似,在指令的 ctor 中注入 ViewContainerRef

constructor(_viewContainerRef: ViewContainerRef)

像这样获取宿主组件:

let hostComponent = (<any>_viewContainerRef)._lContainer[0][8];

答案 8 :(得分:-1)

constructor(private vcRef: ViewContainerRef){
        let parentComponent=(<any>this.vcRef)._view.context;
}