4

I'm using the tab project explained here: Tabs Project

Everything else but my issue works perfectly. The only thing that doesn't work for me, at the moment, is applying *ngFor when creating tabs.

I know that the 2nd-phase checking by angular detects changes, and he's right, the tabs might be adding while the 2nd-phase check is in progress.

What I want to do is to try and still make it work, it's super important to me to use *ngFor within the tabs selector.

Provided is a Plunker code demonstrating the crash and what I'm trying to achieve.

Plunker Code

Important to say, Ive looked into

and I understand that its only on debug mode and what the answerer said, though it was a year ago.

In Addition

Unlike the Plunker which able run the code with errors in the console (that's the explanation of the issue), I cant even switch tabs in my project, but that's a normal behavior, I don't want bad code.

Unfortunately, I cant share my real code because its for my work basically, but I can provide more data if needed, though it is based almost 100% from the Plunker and the project I provided in the beginning of the issue. .

Community
  • 1
  • 1
Ori Refael
  • 2,888
  • 3
  • 37
  • 68
  • I fiddled with this a little and see the problem is in the Tab template, specifically the "active" value. If you sub "true" or "false" for "!active", your problem goes away. Based on the error, it would seem that somehow that value for a given tab is being altered in such a way that Angular borks. I'm fiddling a little more to see if I can resolve it. – Tim Consolazio Dec 18 '16 at 22:02
  • 1
    Also interesting; if, in the Tabs class, if you set the initial value to "false" in the onSelect handler, the problem goes away. I'm betting that's because the initial value is false, and you're setting it to true when the tabs are created/init'd. – Tim Consolazio Dec 18 '16 at 22:06
  • @TimConsolazio i might play with that also just to check if there are more addequate ways to handle that. – Ori Refael Dec 18 '16 at 22:14

2 Answers2

3

One solution is to wrap your "zone" code with setTimeout (other methods for triggering change detection manually will also work)

if(activeTabs.length === 0) {
      setTimeout(()=>{
        this.selectTab(this.tabs.first);
      },0);
}

Full plunker: https://plnkr.co/edit/UVfiJFYexgua2HfPe0Lw?p=preview

eko
  • 39,722
  • 10
  • 72
  • 98
  • before I will offer you to make love to me for solving my 3-4 hours issue. Is there a way which is not a work around that you know of? – Ori Refael Dec 18 '16 at 21:59
  • @OriRefael Haha, this was the first thing that came to my mind. Of course there are other ways, as drewmoore and MarkRajcok explained in more detail. But I didn't want to change the architecture of your code. So.. this is a more "practical" hack/trick. – eko Dec 18 '16 at 22:08
  • 1
    I eventually came to the same solution here: I looked through the component lifecycle hook docs, and what seems to be the problem here is an architectural one, manipulating the bound properties of an object in the tabs early lifecycle hooks would seem to be a no no. There's a number of ways to try and solve this problem but the only one I found that works is the indicated one here. http://www.allenhashkey.com/web-development/angular2/angular-2-expression-changed-after-it-has-been-checked-exception/ – Tim Consolazio Dec 18 '16 at 23:31
1

In order to fix the issue you need to remove the code for setting the first tab to active from your ngAfterContentInit() method. This code is causing the issue:

if(activeTabs.length === 0) {
  this.selectTab(this.tabs.first);
}

I assume that the error pops up because change detection requires that the DOM is stabilized after one run, and your call in the ngAfterContentInit() would require anothed pass of CD to reflect the new tab.active value in the DOM.

What you could do instead is set the first element in your *ngFor to be active by default. Something like:

<tab *ngFor="let item of ['1','2']"; let index = index" [active]="index == 0"...

EDIT: Seems you can also use the first local variable (haven't tried it). See this plunkr

Fjut
  • 1,314
  • 12
  • 23
  • 1
    though you are probably right, I prefer to let the control be in the hands on Tabs.component rather than external one, so I will suffer the cons of writing a work around in my code and chooe @echonax 's answer. – Ori Refael Dec 18 '16 at 22:05