1

I'm getting this exception when I'm trying to do this - messages.component.html:

<div *ngFor = "let message of messages | async">
      <div *ngIf = "needToPrint(message.timestamp | date: 'dd/MM/yy')">
        <p class = "date-stamp"> {{ message.timestamp | date: "MM/dd/yy" }} </p> 
      </div> 
.
.
</div>

needToPrint function - messages.component.ts:

 import { AfterViewChecked, ElementRef, ViewChild, Component, OnInit } from '@angular/core';
import { FirebaseListObservable } from 'angularfire2';
import { AF } from "../../providers/af";
import { ChangeDetectorRef } from "@angular/core";


@Component({
  //selector: 'app-messages',
  templateUrl: './messages.component.html',
  styleUrls: ['./messages.component.css']
})

export class MessagesComponent implements OnInit, AfterViewChecked
{
  @ViewChild('scrollMe') private myScrollContainer: ElementRef;

  savedDate: string = '';

/* some code... */

  // ==================================================

  constructor(public afService: AF, private cdRef:ChangeDetectorRef) 
  {
      this.messages = this.afService.messages;
  }
  // ==================================================
  // If need to print the date ahead

  needToPrint(date)
  {
    if (this.savedDate != date)
    {
      this.savedDate = date;
      return true;
    }

    return false;
  }



  sendMessage()
  {
    this.afService.sendMessage(this.newMessage);
    this.newMessage = '';
  }

  // ==================================================

 ngAfterViewChecked() 
 {
    // this.scrollToBottom();
  }

  scrollToBottom(): void 
  {
    try {
        this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
    } catch(err) { }
  }


}

I understand that this exception is appear only in dev mode but how can I fix it? (I tried to read about this but still, no succeed to fix it).

thanks.

zcbd
  • 85
  • 10
  • This is because you have a function in the template, which causes this. You shouldn't have functions in template, in worst case they create infinite loop and your app crashes. You should handle logic in component and display variables in the template. – AT82 May 07 '17 at 07:03
  • So I read the link below, and understand that one of the solutions is to use setTimeOut, something like this: ngAfterViewInit() { // wait a tick to avoid one-time devMode // unidirectional-data-flow-violation error setTimeout(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth); } but I can't figure how to use it to fit my code? – zcbd May 07 '17 at 20:12
  • As mentioned, I suggest you refactor your code as such that you don't need to use a function in template, as that in the worst case causes an infinite loop :) – AT82 May 08 '17 at 06:03
  • Im not sure that the question was clear so I added it. I hope you can see it again and point on the problem. Thanks!! – zcbd May 08 '17 at 06:15
  • Can you create plunker that will reproduce it? – yurzui May 08 '17 at 08:48

1 Answers1

1

Not tested, but hopefully this helps.

So as mentioned, the error is caused by calling a function in template, i.e

needToPrint(message.timestamp | date: 'dd/MM/yy')

I strongly suggest that you would refactor your code as such that you are not calling a function from the template, it is considered bad practice for change detection reasons, and in worse case it can cause an infinite loop: *ngFor running an infinite loop in angular2

The workaround for this though, could be invoking change detection manually, by using ChangeDetectorRef. Import it, inject in constructor and use it inside needToPrint-function:

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

constructor(private ref: ChangeDetectorRef) { }

needToPrint(date) {
  if (this.savedDate != date){
    this.savedDate = date;
    this.ref.detectChanges();
    return true;
  }
  this.ref.detectChanges();
  return false;
}
Community
  • 1
  • 1
AT82
  • 71,416
  • 24
  • 140
  • 167
  • Thanks for your help, now I'm getting "Maximum call stack size exceeded." :( any suggest how to deal with it? – zcbd May 08 '17 at 07:47
  • This is exactly what happens when having a function in the template, you have created an infinite loop like I warned you about, the `needToPrint`-function is called over and over and over again. You should remove `needToPrint` function from template and use variables in the template. That would probably mean you need to manually subscribe, iterate the messages in ts-file and check `(this.savedDate != date)` and then have a boolean flag instead in the `*ngIf` in the template. – AT82 May 08 '17 at 07:51
  • By the way, what for/why change the value like `this.savedDate = date`? I'm trying to understand this :) – AT82 May 08 '17 at 09:35