我想从服务中打开一个组件,而不必将该组件添加到具有该服务的每个其他组件中。
在服务中我有以下内容:
@Injectable({
providedIn: 'root',
})
export class MyService {
constructor(private appRef: ApplicationRef) {}
createComponent(): void {
const comp = createComponent(MyComponent, {environmentInjector: this.appRef.injector});
this.appRef.attachView(comp.hostView);
comp.call();
}
}
MyComponent 看起来像这样:
@Component({
selector: 'app-my-dialog',
standalone: true,
imports: ...,
template: '... <another-component /> ...',
})
export class MyComponent {
@ViewChild(AnotherComponent) comp!: AnotherComponent;
call(): void {
this.comp.doSomething();
}
}
现在我的问题是,viewChild 未定义。 我不明白为什么它没有正确加载。
我尝试使用 ViewContainerRef,但是我无法创建这样的组件,因为它需要一个只有 ApplicationRef 才有的 EnvironmentInjector。
我知道我可以将 MyComponent 添加到调用组件的 HTML 中,但我宁愿只拥有服务。
我使用 Angular 16.2.10,如果有帮助的话。
在 Angular 中,ViewChild 查询在 ngAfterViewInit 生命周期挂钩之后解析。在您的情况下,当您在附加视图后立即调用 comp.call() 时,MyComponent 的 ngAfterViewInit 钩子可能尚未被调用,导致 this.comp 未定义。
为了解决这个问题,您可以等待 ngAfterViewInit 生命周期钩子被触发,然后再调用 MyComponent 上的方法。您可以通过在服务中使用 AfterViewInit 生命周期挂钩来实现此目的。这是代码的更新版本:
@Injectable({
providedIn: 'root',
})
export class MyService implements AfterViewInit {
private compRef: ComponentRef<MyComponent>;
constructor(private appRef: ApplicationRef, private componentFactoryResolver: ComponentFactoryResolver) {}
ngAfterViewInit(): void {
// This will be called after the view of the service is initialized.
// Now you can safely call methods on MyComponent.
this.comp.call();
}
createComponent(): void {
const compFactory = this.componentFactoryResolver.resolveComponentFactory(MyComponent);
this.compRef = compFactory.create(this.appRef.injector);
this.appRef.attachView(this.compRef.hostView);
// You may need to append the component's host view to the DOM if needed.
// document.body.appendChild(this.compRef.location.nativeElement);
}
get comp(): MyComponent | undefined {
return this.compRef ? this.compRef.instance : undefined;
}
}
现在,您可以使用 myService.createComponent() 创建组件并稍后通过 myService.comp 访问它。初始化 MyComponent 的视图后会触发服务中的 ngAfterViewInit 钩子,确保调用 this.comp.call() 时定义了 this.comp。
另请注意,我已将 AfterViewInit 添加到服务类中,并且存储了对组件的引用 (this.compRef),以便您稍后可以访问它
我现在使用 setTimeout 解决了这个问题。
@Injectable({
providedIn: 'root',
})
export class MyService {
constructor(private appRef: ApplicationRef) {}
createComponent(): void {
const comp = createComponent(MyComponent, {environmentInjector: this.appRef.injector});
this.appRef.attachView(comp.hostView);
// Timeout here!
setTimeout(() => {
comp.call();
}
}
}