在动态创建的组件中未触发ngOnInit [英] ngOnInit is not being triggered in dynamically created component

查看:57
本文介绍了在动态创建的组件中未触发ngOnInit的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to build Info board. Till the layout was static everything was running fine but when I tried to make the layout dynamic (user can define it) the ngOnInit wasn't called in the created components.

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

I have two main components one where user can select for what building he want's to see the board for (SelectBoardComponent) and second that contains the board itself (BoardComponent). When I navigate from the SelectBoardComponent to the BoardComponent everything works. But when I reload or directly access the path for BoardComponent whole Angular cycle (OnInit....) is not triggered in the dynamically added childrens.

我已在 stackblitz

为了能够找到要创建的组件,我创建了一个组件注册表",在其中注册了可以动态呈现的每个组件.

To be able to find out what component I want to create I have created a "Component registry" where I register every component that could be rendered dynamically.

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 我要呈现的组件之一

So I register InfoCardComponent one of the components I want to render

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 指令

In the BoardComponent I add appRender directive to the element I want to display the components inside

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

The appRender handles the process of generating the components. I render the components based on JSON data.

  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,
    };
  }

所以我的问题是:

这是预期的行为还是我忘了一些东西?

So my question is:

Is this an expected behavior or I forgot something?

我很乐意提供帮助.

推荐答案

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

You have an LifeCycleHook problem. You can't access ElementRef's and TemplateRef's in ngOnInit(), because the template is not yet rendered. You should use ngAfterViewInit() LifeCycleHook. Move your logic from ngOnInit() to 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.

setTimeout() is for fixing ExpressionHasBeenChanged-Error.

别忘了实现 AfterViewInit 并将其从 @ angular/core

这篇关于在动态创建的组件中未触发ngOnInit的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆