从指令访问外部组件实例
Access outer component instance from directive
我有一个带有单个输入 [id] 的简单组件,以及一个使用 'my-directive'.
的模板
<div>
<div my-directive>
hello world
</div>
</div>
简单组件将在模板中多次使用:
<simple-component [id]="1"></simple-component>
<simple-component [id]="2"></simple-component>
我可以从每个 my-directive 实例中访问 simple-component 的实例吗?
目标是每个 my-directive 实例都知道它们的 simple-component 'host/outer' 实例,
例如,访问他们的 'id' 属性。
是的,有一种使用 @Host()
分辨率修饰符访问父组件的方法(有关 official documentation). The basic idea is to navigate the component tree by using Dependency Injection in order to find a reference to a parent element from the child element. There are a couple of nice examples here 的更多信息。
默认情况下,Angular 一直向上搜索提供的实例,一直到 NullInjector
(层次结构中的最高层)。如果它没有找到实例,它就会抛出异常,除非我们使用 @Optional
,在那种情况下它会 returns 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,我们从指令。在这里我们不能使用 Host()
因为搜索将以指令作为最后一站停止(并且 AppComponent
在链中更高)。所以我们只是不添加任何东西,我们得到了正确的参考。
希望这对您有所帮助。
您可以使用以下服务实现:
@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
我有一个带有单个输入 [id] 的简单组件,以及一个使用 'my-directive'.
的模板<div>
<div my-directive>
hello world
</div>
</div>
简单组件将在模板中多次使用:
<simple-component [id]="1"></simple-component>
<simple-component [id]="2"></simple-component>
我可以从每个 my-directive 实例中访问 simple-component 的实例吗?
目标是每个 my-directive 实例都知道它们的 simple-component 'host/outer' 实例, 例如,访问他们的 'id' 属性。
是的,有一种使用 @Host()
分辨率修饰符访问父组件的方法(有关 official documentation). The basic idea is to navigate the component tree by using Dependency Injection in order to find a reference to a parent element from the child element. There are a couple of nice examples here 的更多信息。
默认情况下,Angular 一直向上搜索提供的实例,一直到 NullInjector
(层次结构中的最高层)。如果它没有找到实例,它就会抛出异常,除非我们使用 @Optional
,在那种情况下它会 returns 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,我们从指令。在这里我们不能使用 Host()
因为搜索将以指令作为最后一站停止(并且 AppComponent
在链中更高)。所以我们只是不添加任何东西,我们得到了正确的参考。
希望这对您有所帮助。
您可以使用以下服务实现:
@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