从指令访问外部组件实例

时间:2020-02-13 11:51:35

标签: angular angular-directive angular-components

我有一个具有单个输入[id]的简单组件,以及一个使用“我的指令”的模板。

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

简单组件将在模板中多次使用:

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

我是否可以从每个my指令实例中访问简单组件实例?

目标是每个my指令实例都知道其简单组件“主机/外部”实例, 例如,以访问其“ id”属性。

2 个答案:

答案 0 :(得分: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在链中更高)。因此,我们只是不添加任何内容,便获得了正确的参考。

希望这会有所帮助。

答案 1 :(得分:0)

您可以使用服务来实现:

@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