Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tabs] md-tab-link: routerLinkActive.isActive error #1967

Closed
blyndcide opened this issue Nov 23, 2016 · 44 comments
Closed

[Tabs] md-tab-link: routerLinkActive.isActive error #1967

blyndcide opened this issue Nov 23, 2016 · 44 comments
Assignees
Labels
P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@blyndcide
Copy link

Trying to use the new md-tab-nav-bar like this:

<nav md-tab-nav-bar>
	<a md-tab-link routerLink="/admin/management" routerLinkActive #rla="routerLinkActive" [active]="rla.isActive">Management</a>
</nav>

but get an error

TypeError: Cannot read property 'some' of undefined

Seems like this way should work after commit: angular/angular@c9f58cf

@DzmitryShylovich
Copy link

Pls add a plunkr with repro.

@andrewseguin
Copy link
Contributor

Please use the issue template which helps us reproduce and diagnose the issue. Including a plunkr with this bug will help us determine what is happening.

@andrewseguin andrewseguin self-assigned this Nov 29, 2016
@nayfin
Copy link
Contributor

nayfin commented Dec 1, 2016

This is exactly how I am trying to use this feature as well, super easy way to keep your tabs in line with your location. I am working on a plunkr but I am running into some issues just getting it set up.

  1. It seems like the Angular Material 2 template may not be getting the latest version of material, because if I try to add the [active]="someBoolean" directive to the anchor, I get yelled at by the compiler:
    VM1000 zone.min.js:1 Unhandled Promise rejection: Template parse errors:
Can't bind to 'active' since it isn't a known property of 'a'. ("ks" color="primary">
      <a md-tab-link *ngFor="let child of directory; let i = index;"
        [ERROR ->][active]="routerLinkActive"
        (click)="activateLink(i)">

I am assuming this is because it is a pretty new feature. I looked through systemjs.config and it seems to be asking for the latest, so I don't know where to go from here.

  1. I set up the router in main.ts, but I am stuck. I think I set up everything correctly but am getting some errors.

If someone can help me resolve these two issues I can finish up the plunkr and we can get this feature working.

@nayfin
Copy link
Contributor

nayfin commented Dec 4, 2016

Thanks for the fast update. I updated the plunkr. All the relevant code is in app.component.html. I commented the hell out of it explaining what we are looking for. Hopefully it is just a syntactic mistake. Please let me know if I can help in any way. Thanks for all your hard work, this is an amazing set of components and I hope that I can contribute soon.

@nayfin
Copy link
Contributor

nayfin commented Dec 5, 2016

I just tried adding the safe navigation operator to the rla template variable i.e. [active]="rla?.isActive. I was hoping that perhaps it just need time to load its values, but no dice, I am still getting the same error messages.

@paladinu
Copy link

paladinu commented Dec 6, 2016

I'm getting the same error when trying to use the value in to set a class:
[ngClass]="{'icon icon_contact' : (rla && rla.isActive)}"
Results in
core.umd.js:2838 EXCEPTION: Error in /js/app/templates/nav-primary.html:18:38 caused by: Cannot read property 'some' of undefinedErrorHandler.handleError ....

@nayfin
Copy link
Contributor

nayfin commented Dec 7, 2016

If you just need to add a class to the active link and remove it when it's not active, Angular has a built in directive that does that.

<a routerLink="/someUrl" [routerLinkActive]="['class1', 'class2']"> A cool link </a>

So, yours would be:

<a routerLink="/someUrl" [routerLinkActive]="['icon ', 'icon_contact']"> A cool link </a>

Here are some docs that might help.

Here is a plunkr where .class1 and .class2 follow the active link.

You will notice that it updates the classes even if the redirect comes from a link outside of the md-tab-link, but Material's border-bottom only registers ng-reflect-active="true" if redirected by the md-tab-link. Though it would follow the active link if we can get [active]="rla.isActive" to work.

@paladinu
Copy link

paladinu commented Dec 7, 2016

Yes, I use that else where. The problem I am trying to solve is that I want to apply a class to something that is a child or my routerlink. Like this:
<a routerLink="/someUrl"><span [routerLinkActive]="['class1', 'class2']">I want the class on this span</span></a>
From what I can tell that is what the RouterLinkActive directive export was trying to solve and it works fine if I throw it in as interpolation like class="{{rla.isActive?'icon_contact':''}}", but blows up when I try to use it inside the ngClass directive with the rla variable. If that routerLinkActive is supposed to work on children and I'm just doing something wrong please let me know.

@nayfin
Copy link
Contributor

nayfin commented Dec 7, 2016

If you can update that plunkr to demonstrate what you are looking for, I think I should be able to help you.

@paladinu
Copy link

paladinu commented Dec 7, 2016

Here is a fork of that plunkr. My goal is to be able to apply a class to something within the router link rather than the routerlink itself. I think it is the same issue as the OP because they are both trying to access the isActive within an expression.

http://plnkr.co/edit/cUl8VS?p=preview

@nayfin
Copy link
Contributor

nayfin commented Dec 8, 2016

Okay... I've done everything I can think of. It seems these particular attributes ,[active] and [ngClass], don't want to wait for rla.isActive to load and return a boolean. They won't wait even when using the Safe Navigation Operator ?., which is explicitly telling them to.

You can however use class bindings even in the child of the element containing routerLinkActive:

<a md-tab-link routerLink="someLink" routerLinkActive #rla="routerLinkActive"> <span [class.someClass]="rla.isActive">Working child link</span> </a>

I would argue that this is actually cleaner than using ngClass. Here is the plunkr.

Can anyone help me get the same functionality out of [active]='rla.isActive'? I can't understand why binding to one attribute works perfectly while binding to another refuses to.

@nayfin
Copy link
Contributor

nayfin commented Dec 16, 2016

@DzmitryShylovich will your pull request #13341 address these issues?

@DzmitryShylovich
Copy link

@nayfin yes, but it's not clear when it will be merged

@antonmoiseev
Copy link

@DzmitryShylovich AFAICS your PR doesn't introduce backwards incompatible changes at the moment, please correct me if I'm wrong.

@andrewseguin considering @DzmitryShylovich's PR is safe to merge, could you raise the priority of this issue. I believe this is pretty much how tabs component will be used in the most of the apps.

/cc @jelbourn

P.S. Sorry for bothering during the holidays 😉. Happy Christmas! 🎄 🎅

@jheinnic
Copy link

jheinnic commented Jan 2, 2017

Good afternoon!

I just ran into this very same issue today. When debugging my own code stil left me blocked, I turned to the web and found this thread. It changed the way I was looking at the issue and that helped me find a way to work around it in the immediate term.

I haven't enough time to draft an example at the moment. I will follow up with one later on if this description is unclear, but want to offer what I have now in the meantime.

It looks like the problem here is that the RouterLinkActive.isActive() method is dependent upon a @ViewChildren query-induced injection to occur. AFAIK, that happens during the content initialization stage of compilation. The property annotated with @ViewChildren is "this.links".

The call to RouterLinkActive.isActive( ) is part of the same content model where RouterLinkActive is getting links injected from RouterLink. which explains why "this.links.changes.some( )" causes "Cannot read property 'some' of undefined" to be thrown--it isn't injected by the time the first call to isActive() expects it to be there, and the implementation of isActive( ) doesn't have a defensive check allowing it to return false when it's called so early.

I confirmed that adding that undefined check in isActive( ) solved my issue, but modifying the framework isn't really a viable workaround. I couldn't make the framework tolerate an early call to isActive(), so I forced my component to defer its first call.

Before workaround:
<a md-tab-link routerLink="/lobby" routerLinkActive #rla="routerLinkActive" [active]="rla.isActive">Lobby</a>

After workaround:
<a md-tab-link routerLink="/lobby" routerLinkActive #rla="routerLinkActive" [active]="rlaSafe && rla.isActive">Lobby</a>

And in the Component code

export class MyNavbarComponent implements AfterViewInit {
  private rlaSafe: boolean = false;

  public ngAfterViewInit() {
    this.rlaSafe = true;
  }

The additional boolean stops the flow control from reaching isActive() until after the view initialization lifecycle event has passed, by which time the response to RouterLinkActive's @ViewChildren query has been injected and the call to isActive( ) therefore no longer gets tripped up on an uninitialized reference.

Its not transparent, so its a work-around and not a solution, but it unblocked the rest of what I have yet to do early this week, so I'm comfortable with it for now.

@Splaktar
Copy link
Member

Splaktar commented Jan 3, 2017

@jheinnic thanks. That's very helpful as I ran into this today as well!

I had to make a small change to this to keep zone.js happy.

  private rlaSafe: boolean = false;

  constructor(private changeDetectionRef: ChangeDetectorRef) {}

  public ngAfterViewInit() {
    this.rlaSafe = true;
    // Fix "Expression has changed after it was checked" exception from Zone.js
    this.changeDetectionRef.detectChanges();
  }

@jelbourn
Copy link
Member

jelbourn commented Jan 3, 2017

@andrewseguin This looks like it's a bug in Angular, specifically router_link_active.ts. The function:

  private hasActiveLink(): boolean {
    return this.links.some(this.isLinkActive(this.router)) ||
        this.linksWithHrefs.some(this.isLinkActive(this.router));
  }

Should guard against links and linksWithHrefs being null/undefined. This is something we could fix.

@ChuckkNorris
Copy link

Just a note: each <a md-tab-link></a> element must have a unique variable name for its routerLinkActive object or it will not correctly set its tab to active.

Is there maybe a way to reference specifically the element variable (e.g. #rla="routerLinkActive" [active]="#rla.isActive")

app.component.html

<nav md-tab-nav-bar>
     <a md-tab-link routerLink="browse-pets" routerLinkActive #rla="routerLinkActive" [active]="rlaSafe && rla.isActive">Browse Pets</a>
     <a md-tab-link  routerLink="/sell-pet"  routerLinkActive #rlab="routerLinkActive" [active]="rlaSafe && rlab.isActive">Sell Pet</a>
</nav> 

app.component.ts

import { Component, AfterViewInit, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit{

  constructor(private changeDetectionRef: ChangeDetectorRef) {}
  private rlaSafe: boolean = false;

  ngAfterViewInit() {
    this.rlaSafe = true;
    this.changeDetectionRef.detectChanges();
  }
}

@nayfin
Copy link
Contributor

nayfin commented Jan 6, 2017

Thanks for the help and simple workaround until merge. Here is a plunkr of workaround for anyone who would like a full example.

@somombo
Copy link

somombo commented Jan 7, 2017

Hi everyone, I found this error after a sleepless night of thinking I was doing something wrong while trying to implement the documented MdTabNavBar Directive example shown at the bottom of https://material.angular.io/components/component/tabs#tabs-and-navigation (under "Tabs and Navigation")..

Obviously, since this is still an open issue, that example (written verbatim) wont work.
I think it would be great if someone put a quick note next to that example pointing to this issue so that people dont waste too much time thinking they are not following what they may think is documented correctly. Something like the following would suffice.

Note: In it's current form, example above has a known bug and will not work, please consult Issue #1967 for more details and for for a work around whilst this issue is being resolved by the Angular team.

@somombo
Copy link

somombo commented Jan 7, 2017

After racking my head quite a while, I came to the same conclusion as @jelbourn. I think it's essential that in RouterLinkActive.hasActiveLink(), the values RouterLinkActive.links and RouterLinkActive.linksWithHrefs are checked for null/undefined values as a guard, before doing the activeLink testing.

Has anyone raised this as an issue in the https://github.com/angular/angular repo yet? Or perhaps created a quick PR?

@somombo
Copy link

somombo commented Jan 7, 2017

I would additionally like to suggest the change below to the MdTabLink class in this repo.
The reason again is for safety, so that we are only updating the nav bar's active link when the view / content is ready. Haven't had time to test yet but, what do you all think?

(Refer to: tab-nav-bar.ts)

/* ... */
// New MdTabLink
@Directive({
  selector: '[md-tab-link], [mat-tab-link]',
})
export class MdTabLink  implements AfterContentInit {
  private _isActive: boolean = false;

  /** Whether the link is active. */
  @Input()
  get active(): boolean {
    return this._isActive;
  }
  set active(value: boolean) {
    this._isActive = value;
    this._setActiveNavLink();
  }
  
  //flag to help double check that nav update only occurs when content is ready
  private _contentReady: boolean = false;
 
  ngAfterContentInit(){
    this._contentReady = true;
    this._setActiveNavLink();
  }

  private _setActiveNavLink(){
    if (this.active && this._contentReady) {
      this._mdTabNavBar.updateActiveLink(this._element.nativeElement);
    }
  }

  constructor(private _mdTabNavBar: MdTabNavBar, private _element: ElementRef) {}
}

@Splaktar
Copy link
Member

Splaktar commented Jan 7, 2017

@somombo yes, there is a PR open in the @angular/angular repo. It's been reviewed and discussed for about a month now.

Splaktar added a commit to DevIntent/components that referenced this issue Jan 7, 2017
add info to docs to avoid more people hitting this bug
there have been quite a few reports about this recently
it is not obvious to users that it is a core bug and not a bug in their code

Relates to angular#1967
@vinagreti
Copy link
Contributor

Should we mention in the docs this rlaSafe workaround? I've spent a lot of time figuring out what I was doing wrong... There is differences between git Readme.md, material.angular.io docs and what we really should do.

Just say the words and I can update the README.md to point this issue and th rlaSafe workaround.

@Splaktar
Copy link
Member

@vinagreti It does not look like they want to update any of the docs to address this bug. Instead they are trying to get the bug resolved ASAP. My PR to update the docs was declined.

It looks like the PR in the core is waiting on review still.

@blyndcide
Copy link
Author

Angular 2.4.4 fixes the error and tabs change successfully; however [active] is not being set.

@adrm
Copy link

adrm commented Feb 2, 2017

Setting version to Angular 2.4.5 fixed it for me!

@blyndcide
Copy link
Author

Ok I got [active] set correctly this way:

<nav md-tab-nav-bar>
	<a md-tab-link routerLink="/admin/management" routerLinkActive #rla1="routerLinkActive" [active]="rla1.isActive">Management</a>
	<a md-tab-link routerLink="/admin/profile" routerLinkActive #rla2="routerLinkActive" [active]="rla2.isActive">Profile</a>
</nav>

Thanks to all who helped fix this.

@aretheregods
Copy link

This still seems to be an open issue on Angular 4 with Angular Material 2.0.0-beta5. I could get neither the example on material io nor the fix proposed in this thread to resolve the issue. The browser console says Can't bind to 'active' since it isn't a known property of 'a'.

@kfirufk
Copy link

kfirufk commented Sep 5, 2017

angular 4.4.0-RC0 and material 2.0.0-beta.10 and this issue still occurs. none of the work-arounds work. any ideas ? is there another way to add components to each selected tab besides ? what do I do ?

@toonvanstrijp
Copy link

toonvanstrijp commented Sep 20, 2017

It's still an issue. Im using the following routes:

{
    path: 'organisation/:id',
    component: OrganisationComponent,
    children: [
      {path: '', component: OrganisationEditComponent},
      {path: 'locations', component: LocationsComponent},
    ]
  }

Then this as my component html:

<nav md-tab-nav-bar>
  <a md-tab-link
     [routerLink]="['./']"
     routerLinkActive #rla="routerLinkActive"
     [active]="rla.isActive">Algemeen</a>
  <a md-tab-link
     [routerLink]="['./locations']"
     routerLinkActive #rlb="routerLinkActive"
     [active]="rlb.isActive">Locaties</a>
</nav>
<router-outlet></router-outlet>

So now it works on the initial load, but whenever I go back to the './' route the md-tab-link active doesn't get updated.

UPDATE:

I've fixed it by add this [routerLinkActiveOptions]="{exact: true}" to the first link, can someone explain why this is needed?

@dinvlad
Copy link

dinvlad commented Oct 12, 2017

Seems like recent deprecation of md- prefix broke the templates. For anyone still struggling with these, try using mat-..

@ravishivt
Copy link

Same issue with latest Angular 5.1.2 and Angular Material 5.02. My hacky/flaky workaround:

  ngAfterViewChecked() {
    setTimeout(() => {
      this.changeDetectionRef.detectChanges();
    });
  }

@reothues
Copy link

reothues commented Jan 18, 2018

@aretheregods Same issue with latest Angular 5.2.1 and Angular Material 5.1.0;
I believe it's that [active] should be bond to mat-tab-link directive but instead it was bond to <a>
so any workaround would be to delay the [active] as later than mat-tab-link had bootstrapped

Edit:
it seems mat-tab-link has more problem than [active]. style looks weird too

@blyndcide blyndcide reopened this Jan 18, 2018
@andrewseguin andrewseguin changed the title md-tab-link: routerLinkActive.isActive error [Tabs] md-tab-link: routerLinkActive.isActive error Jan 23, 2018
@andrewseguin andrewseguin added the P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent label Jan 23, 2018
@jelbourn
Copy link
Member

This should be long ago fixed in Angular.

@reothues
Copy link

reothues commented Feb 4, 2018

and i'm still having it

@sopretty
Copy link

sopretty commented Feb 9, 2018

I'm having the same problem here :(

@aretheregods
Copy link

@jelbourn This is still an open issue for me on the latest version. Please consider reopening this.

@andrewseguin andrewseguin reopened this Feb 13, 2018
@hectorggp
Copy link

I have the same problem here 👎

@jotatoledo
Copy link

jotatoledo commented Mar 19, 2018

Having a similar issue, sadly I dont have the time to create a repro.
The method suggested by @ravishivt worked for me as workaround

@andrewseguin
Copy link
Contributor

If you are affected, please re-open if you can provide a reproduction of the issue, otherwise we won't be able to investigate.

@AsantosCandor
Copy link

Having the same error.

@beeman
Copy link

beeman commented Jun 15, 2018

In my case the error was fixed by importing MatTabsModule from @angular/material

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests