Angular - 动态创建的组件的 ViewChild 未定义

问题描述 投票:0回答:2

我想从服务中打开一个组件,而不必将该组件添加到具有该服务的每个其他组件中。

在服务中我有以下内容:

@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 dynamic angular-components viewchild angular-dynamic-components
2个回答
0
投票

在 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),以便您稍后可以访问它


0
投票

我现在使用 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();
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.