【问题标题】:ngOnInit is not being triggered in dynamically created componentngOnInit 未在动态创建的组件中触发
【发布时间】:2019-09-03 17:57:17
【问题描述】:

我正在尝试构建信息板。直到布局是静态的,一切都运行良好,但是当我尝试使布局动态化(用户可以定义它)时,ngOnInit 没有在创建的组件中调用。

我有两个主要组件,其中一个用户可以选择他想要查看的板(SelectBoardComponent),第二个包含板本身(BoardComponent)。当我从SelectBoardComponent 导航到BoardComponent 时,一切正常。但是当我重新加载或直接访问BoardComponent 的路径时,整个 Angular 循环 (OnInit....) 不会在动态添加的子项中触发。

我已经在stackblitz上转载了这个问题

代码

为了能够找出我想要创建的组件,我创建了一个“组件注册表”,我在其中注册了每个可以动态呈现的组件。

import {Type} from '@angular/core';

type ComponentClass = Type<any>;

const REGISTRY = new Map<string, ComponentClass>();

export const getTypeFor = (name: string): ComponentClass => {
  return REGISTRY.get(name);
};
export const Register = () => {
  return (cls: ComponentClass) => {
    REGISTRY.set(cls.name, cls);
  };
};

所以我注册了InfoCardComponent 我要渲染的组件之一

import { Component, OnInit } from '@angular/core';
import {Register} from '../register';

@Component({
  selector: 'app-info-card',
  templateUrl: './info-card.component.html',
  styleUrls: ['./info-card.component.css']
})
@Register()
export class InfoCardComponent implements OnInit {
  public called = "not called!";
  constructor() { }

  ngOnInit() {
    this.called = "called!";
  }
}

BoardComponent 中,我将appRender 指令添加到要在其中显示组件的元素中

appRender 处理生成组件的过程。 我根据 JSON 数据渲染组件。

  private components = [
  {
    "id": 0,
    "position": 0,
    "cells": [
      {
        "id": "info",
        "title": "Info panel",
        "component": "InfoCardComponent",
        "childs": []
      }
    ]
  }
];
  private componentsRef = {};

  constructor(
    private appRef: ApplicationRef,
    private injector: Injector,
    private componentFactory: ComponentFactoryResolver,
    private renderer: Renderer2,
    private el: ElementRef) {
  }

  ngOnInit() {
      this.el.nativeElement.innerHTML = '';
      const {rows} = this.render();
      for (const row of rows) {
        this.renderer.appendChild(this.el.nativeElement, row);
      }
  }

  render() {
    const grid = {rows: []};
    for (const c of this.components) {
      const cells = c.cells;
      if (cells.length > 0) {
        const row = this.renderer.createElement('div');
        for (const {component} of cells) {
          const cell = this.renderer.createElement('div');
          const card = this.renderer.createElement('div');
          this.renderer.appendChild(card, this.createAngularComponent(component).elem);
          this.renderer.appendChild(cell, card);
          this.renderer.appendChild(row, cell);
        }
        grid.rows.push(row);
      }
    }
    return grid;
  }
  private createAngularComponent(parentComponent) {
    const componentElem = this.componentFactory.resolveComponentFactory(getTypeFor(parentComponent)).create(this.injector);
    this.componentsRef[parentComponent] = componentElem;
    this.appRef.attachView(componentElem.hostView);
    return {
      elem: (componentElem.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement,
    };
  }

所以我的问题是:

这是预期的行为还是我忘记了什么?

我很乐意提供任何帮助。

【问题讨论】:

  • 在我的应用程序中,正在调用 ngOnInit 但未调用 ngAfterViewInit。

标签: angular typescript


【解决方案1】:

您遇到了 LifeCycleHook 问题。您无法访问 ngOnInit() 中的 ElementRef 和 TemplateRef,因为模板尚未呈现。你应该使用ngAfterViewInit()LifeCycleHook。将您的逻辑从 ngOnInit() 移动到 ngAfterViewInit()

做什么:

ngOnInit() 更改为ngAfterViewInit()

ngAfterViewInit() {
      setTimeout(() => {
        this.el.nativeElement.innerHTML = '';
        const {rows} = this.render();
        for (const row of rows) {
          this.renderer.appendChild(this.el.nativeElement, row);
        }
      }, 0)
  }

setTimeout() 用于修复 ExpressionHasBeenChanged-Error。

不要忘记实现 AfterViewInit 并从 @angular/core 导入它

【讨论】:

  • 在我的项目中,我在 AfterViewChecked 内部调用它,为什么那里也需要超时?
  • @JosefKatič 这是一个 ChangeDatection 问题。在本文中,您将找到有关该错误和修复可能性的更多解释和信息:blog.angularindepth.com/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-05
  • 2021-11-04
  • 2019-11-18
  • 1970-01-01
  • 1970-01-01
  • 2019-08-05
  • 1970-01-01
相关资源
最近更新 更多