0

I having some issues with text.component (selector app-text) initialising in the middle of the app life time (not just when I create it).

This is app.component.html:

  <div class="container-fluid" *ngFor="let text of texts;let i=index">
    <app-text (textInfoEmitter)="dataFromChild($event)" [elementId]=i  [ngStyle]="{'transform': getRotation(i), 'font-size.px':getFontSize(i)}" ></app-text>
  </div>

I am emitting the data from app-text with textInfoEmitter function and updating the model in app.component.ts with dataFromChild(e).

I noticed that everytime textInfoEmitter emits, app-text gets reinitialised. I can confirm that because, ngOnInit and constructor functions get called.

This is app.component.ts file:

export class AppComponent implements OnInit {
  texts: [TextModel];

  ngOnInit() {
    this.texts = [
      {
        rotation: 30,
        fontSize: 16,
        offset: { left: 120, top: 200 },
        scale: 1.4
      }
    ]
  }

  dataFromChild(e) {
    this.texts[e.elementID] = {
      rotation: e.rotation,
      fontSize: e.fontSize,
      offset:e.offset,
      scale: 1
    }

  }
  getRotation(i: number) {
    return "rotate(" + this.texts[i].rotation + "deg)";
  }
  getFontSize(i: number) {
    return this.texts[i].fontSize;
  }
}

The text.component.ts is convoluted and I haven't found a way to replicate a complex Angular project online. This is the emit helper function I call:

  emitData(e){
    if ($(e).hasClass("h")){
      this.parentId = $(e).attr("id");
    }else{
      this.parentId = $(e).parent().attr("id");
    }

    this.textInfoEmitter.emit(
      {
        elementID: this.parentId,
        rotation: this.delta.circleX,
        fontSize: this.delta.fontSize,
        offset: {
          left: this.drag.x,
          top: this.drag.y
        }
      }
    );
  }

delta and drag are models in text.component.ts.

My question is, in what situations does a component get reinitialised during lifetime? How can this be prevented?

Replica of a problem. I am essentialy doing the same, using ngFor directive and updating the model with EventEmitter. This time, ngOnInit does not get fired when the model updates.

app.component.ts

import { Component } from '@angular/core';
declare var $;
@Component({
  selector: 'app-root',
  template: `
  <div class="container-fluid" *ngFor="let text of texts; let i = index">
<app-text id="i" (dataEmitter)="setData($event)" [ngStyle]="{'transform': getRotation()}"></app-text>
</div>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  texts = [
    {rotate:10}
  ];

  ngOnInit(){
  }
  getRotation(){
    return "rotate("+this.texts[0].rotate+"deg)";
  }
  setData(e){
      this.texts[0].rotate = e;
  } 
}

text.component.ts

import { Component, OnInit, Output, EventEmitter } from '@angular/core';
declare var $;
@Component({
  selector: 'app-text',
  template:`<div tabindex="-1" (mousedown)="mousedown($event)" (focusout)="focusout($event)">Text</div>` ,
  styleUrls: ['./text.component.css']
})
export class TextComponent implements OnInit {

@Output() dataEmitter = new EventEmitter();
  constructor() { }

  ngOnInit() {
    console.log("ng on Init");
  }
  mousedown(e){
    $(e.target).focus();
    $(e.target).addClass("selected");
    this.dataEmitter.emit(50);
  }
  focusout(e){
    console.log("f out");
    $(e.target).removeClass("selected");
  }
}
sanjihan
  • 5,592
  • 11
  • 54
  • 119

2 Answers2

1

you are changing your this.texts property which is that *ngFor binds to which is, in turn, a parent of your app-text component.

what that means is:

  1. you change this.texts
  2. your DOM elements created by *ngFor for previous value of this.texts gets removed (together with all app-text components which are children)
  3. new iteration through this.texts happens with new instances of app-text for each
dee zg
  • 13,793
  • 10
  • 42
  • 82
  • I've added another code, which is a replica of a problem. I am using ngFor and emitting data, just a s before. But in this case, ngOnInit does not get fired when the model updates. – sanjihan Sep 29 '17 at 09:26
0

Thanks to @dee zg the problem was solved.

When model changes, Angular does indeed reinitialise the whole contents of *ngFor directive. Under the hood, it tries to determine if it needs to destroy and create dom elements.

If you modify the whole model object, angular will create fresh dom elements, but if you modify just properties of the model object, it will not.

For example:

this.texts[0] = {rotation:0, fontSize:23} will cause the contents of ngFor to be destroyed and recreated.

But if you use this:

this.texts[0].rotation = 0;
this.texts[0].fontSize = 23;

it will not.

sanjihan
  • 5,592
  • 11
  • 54
  • 119