从指令访问外部组件实例

Posted

技术标签:

【中文标题】从指令访问外部组件实例【英文标题】:Access outer component instance from directive 【发布时间】:2020-05-29 02:57:42 【问题描述】:

我有一个带有单个输入 [id] 的简单组件,以及一个使用“my-directive”的模板。

<div>
    <div my-directive>
        hello world
    </div>
</div>

simple-component 将在模板中多次使用:

<simple-component [id]="1"></simple-component>
<simple-component [id]="2"></simple-component>

我是否可以从每个 my-directive 实例中访问 simple-component 的实例?

目标是每个 my-directive 实例都知道它们的简单组件“主机/外部”实例, 例如,访问他们的 'id' 属性。

【问题讨论】:

在这里查看我的示例:***.com/questions/53057340/… 【参考方案1】:

是的,有一种方法可以使用@Host() 分辨率修饰符访问父组件(有关official documentation 的更多信息)。基本思想是通过使用依赖注入来导航组件树,以便从子元素中找到对父元素的引用。有几个很好的例子here。

默认情况下,Angular 会向上搜索提供的实例直到 NullInjector(层次结构中的最高层)。如果它没有找到实例,它就会抛出异常,除非我们使用@Optional,在这种情况下它会返回null。 在您的具体示例中,我们使用 Host() 告诉 Angular 停止搜索,将此组件作为搜索时的最后一站。我们不必使用它,即使我们省略了Host(),它仍然可以工作。

my-directive.directive.ts 文件中:

constructor(@Host() private parent: SimpleComponent) 
    // here we have an instance of SimpleComponent and we can access its properties except for the Input properties, those are still not set in constructor

我创建了一个简单的 stackblitz 示例来演示这一点。

编辑:这是example,我们在其中找到AppComponent 的实例,它是指令SimpleComponent 的父级。这里我们不能使用Host(),因为搜索会以指令作为最后一站停止(并且AppComponent 在链中更高)。所以我们只是不添加任何东西,我们得到了正确的参考。

希望这会有所帮助。

【讨论】:

【参考方案2】:

您可以使用服务来实现:

@Injectable()
export class IdsTrackerService 
  private ids$$: BehaviorSubject< [key: string]: true > = new BehaviorSubject(
    
  );
  public ids$: Observable<string[]> = this.ids$$
    .asObservable()
    .pipe(map(ids => Object.keys(ids)));

  public addId(id: string): void 
    if (!this.ids$$.value[id]) 
      this.ids$$.next(
        ...this.ids$$.value,
        [id]: true
      );
    
  

然后在您的指令中,您只需在创建指令时注册一个 ID,并在销毁时取消注册:

@Directive(
  selector: "[appTrackId]"
)
export class TrackIdDirective implements OnInit, OnDestroy 
  @Input() appTrackId: string | undefined;

  constructor(private idsTrackerService: IdsTrackerService) 

  public ngOnInit(): void 
    if (!this.appTrackId) 
      throw new Error(`When using "appTrackId" please pass the ID as argument`);
    

    this.idsTrackerService.addId(this.appTrackId);

    // you now also have access to
    // this.idsTrackerService.ids$ :) !
  

  public ngOnDestroy(): void 
    this.idsTrackerService.removeId(this.appTrackId);
  

不要忘记在组件级别而不是模块级别或全局提供服务:

@Component(
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
  // declare the provider on a component basis
  // so you get a different instance of the service per component
  providers: [IdsTrackerService]
)

这是一个带有调试视图的现场演示,用于显示已注册的 ID:https://stackblitz.com/edit/angular-jyvezk

【讨论】:

以上是关于从指令访问外部组件实例的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Angular 中的依赖注入将属性指令实例传递给嵌套组件

Vue.js 实例方法

Vue.js 实例方法

Vue.js 实例方法

如何获取组件数据?如何从外部访问组件?

如何从外部函数访问 UI 组件?