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

Angular 如何从指令访问主机组件?,angular,angular-directive,Angular,Angular Directive,假设我有以下标记: <my-comp myDirective></my-comp> 是否有任何方法可以从指令访问组件实例 更具体地说,我希望能够从MyDirective访问MyComponent的属性和方法,理想情况下无需向上面的HTML添加任何内容您只需注入它即可 class MyDirective { constructor(private host:MyComponent) {} 一个严重的限制是,您需要提前知道组件的类型 另见 当您事先不知道类型时,它

假设我有以下标记:

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

是否有任何方法可以从指令访问组件实例

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

您只需注入它即可

class MyDirective {
  constructor(private host:MyComponent) {}
一个严重的限制是,您需要提前知道组件的类型

另见

当您事先不知道类型时,它还提供了一些解决方法。

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

在构造函数中插入
ViewContainerRef

constructor(private _viewContainerRef: ViewContainerRef) { }
然后使用

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

这是从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");
                }
         }
学分:

构造函数(专用vcRef:ViewContainerRef){
让parentComponent=(this.vcRef)。\u view.context;
}

如果要在自定义组件上使用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... :-)
  }

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


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

我喜欢这样,它适用于Angular 9

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



可以使用ViewContainerRef访问主机组件

constructor(private el: ViewContainerRef) {}

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

参考:

使指令通用的一个可能的解决方法是将组件的模板ref作为@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];
    }
 }
}
视图中的用法:


属性,以获取对指令实例的引用



谢谢你,甘特,这很有效。理想情况下,我需要一个适用于任何组件的通用解决方案。事实上,对于我在这里试图做的事情,您可能有一个建议:许多人都要求一个通用的解决方案(正如您在链接的问题中所看到的),但目前没有简单的解决方案。@GünterZöchbauer我想我们可以使用一个接口,例如
构造函数(私有主机:HostedComponentInterface){
并要求指令的用户实现该接口??TypeScript接口在运行时不存在,因此DI不支持该接口。@Emobe我没有遇到过很多找不到解决方案的情况。这是最困难的一次。对其他人来说,通常有很好的解决办法。大多数设计决策都是为了提高效率,我认为这是值得的。虽然这段代码片段可以解决这个问题,但它确实有助于提高文章的质量。请记住,您将在将来为读者回答这个问题,而这些人可能不知道您的代码建议的原因。
\u view.context
意味着
\u view
应该是私有的,因此您似乎是以非标准的方式这样做的,就像对以后遇到此问题的人发出的一声喊叫一样。引用:从Angular 9.0.0-next.3开始,此解决方案不再有效。你知道他们现在可能把它藏在哪里了吗?@Fredrik_Macrobond.last(this.viewContainerRef['''u hostView'][0]。\uuuu ngContext\uu)有人找到了一个适合常春藤的解决方案吗?Angular10A含有Angular 12:
(this.viewContainerRef如有)的非常脏的溶液。\u hostLView[8]
。8引用中的上下文(内联)以谨慎使用。这看起来是我的完美解决方案,它有任何缺点吗?这就是我需要引用DirectiveMember中的任何组件以使抽象类可注入的原因,否则您的指令中会出现错误。例如,将
@Injectable({providedIn:'root'})
作为属性添加到摘要中class@SlavaFominII请仔细查看,没有使用私有API。请保持更正,抱歉。组件的外观把我弄糊涂了。
@Directive({selector: 'input-error,input-password,input-text'})
export class FormInputDirective {
  component:FromItemComponentBase;

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

ngOnInit() {
    const _component = this.el && this.el.injector && this.el.injector.get(MyComponent);
}
@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];
    }
 }
}