michal_d
michal_d

Reputation: 33

How to bind 'disabled' property of PrimeNG MenuItem?

I'm developing an application using angular / primeng and came across an issue.

I'm using a primeng TabMenu component and would like some tabs to be disabled depending on whether a user is logged in or not, but I can't for the life of me figure out how to bind a 'disabled' property of a tab to a variable.

Here is my code:

component.html:

<p-tabMenu [model]="items">
</p-tabMenu>

component.ts:

export class TopMenuComponent implements OnInit {

    constructor(private auth: AuthService) { }

    items: MenuItem[];

    ngOnInit() {
        this.items = [
            {label: 'Home', routerLink: '/'},
            {label: 'Tab1',  routerLink: '/tab1'},
            {label: 'Tab2',  routerLink: '/tab2', disabled=!this.auth.isUserLoggedIn},
            {label: 'Tab3',  routerLink: '/tab3'},
        ];

    }
}

where auth.isUserLoggedIn is a boolean changing value depending on user logging in or logging out.

The above of course doesn't work since the 'disabled' property is only set during init to a false value(as expected since user is not logged in yet).

I cannot set 'disabled' to a function or Observable since MenuItem only accepts boolean values.

I actually solved it by creating an observable isUserLoggedInObservable and subscribing to it in ngOnInit() this way:

this.auth.isUserLoggedInObservable.subscribe(value => {
          this.items[2].disabled = !value;
        })

but it feels to me that I must be missing the correct way of solving the issue.

How should it be done correctly?

Upvotes: 3

Views: 7699

Answers (3)

Moh
Moh

Reputation: 117

The issue is that the menu item does not redender if one of its property change.

You still could solve this using CSS:

  1. create an async conditional ngClass in your html:
<p-sidebar>
    <p-menu [model]="menuItems" [ngClass]="{'authenticated': (authState$ | async)}"></p-menu>
</p-sidebar>
  1. add a styleClass "only-authenticated" to the menu you want to hide:
  menuItems: MenuItem[] = [
    {
      label: 'help',
      icon: 'fal fa-question-circle',
      routerLink: '/help',
      styleClass: "only-authenticated"
    }
...
// import your Observable
  authState$ = this.authService.authState$;

  1. finally add the correct styling to your css:
p-menu:not(.authenticated) {
  // user is not authenticated
  .only-authenticated {
    display: none;
  }
}

Upvotes: 1

Md Rafee
Md Rafee

Reputation: 5530

you should set the value of disabled to true if you don't want to show the tab if you are not logged in. So, the code will be:

this.items = [
  {label: 'Home', routerLink: '/'},
  {label: 'Tab1',  routerLink: '/tab1'},
  {label: 'Tab2',  routerLink: '/tab2', disabled= true},
  {label: 'Tab3',  routerLink: '/tab3'},
];

Then you will check if the user is loggedIn. If the user is logged in, you turn it to false. So, the code will be:

if (this.auth.isUserLoggedIn) {
  this.items[2].disabled = false
} else {
  this.items[2].disabled = true
}

This should be the right way what you wanted.

Edit

If you want to make it changeable, you should use Subject;

auth.service.ts:

isLoggedIn = new Subject<boolean>;

islogged() { 
  if (islogged){ \\condition for your loggedIn check
    this.isLoggedIn.next(true);
  } else {
    this.isLoggedIn.next(false);

component.ts

  ngOnInit() {
    this.items = [
      {label: 'Home', routerLink: '/'},
      {label: 'Tab1',  routerLink: '/tab1'},
      {label: 'Tab2',  routerLink: '/tab2', disabled= true},
      {label: 'Tab3',  routerLink: '/tab3'},
    ];
    this.auth.isLoggedIn.subscribe((res:boolean) => this.item[2].disabled = res);

Upvotes: 1

Joep Kockelkorn
Joep Kockelkorn

Reputation: 302

You should avoid subscribing manually when possible, and if you do then you need to cleanup your subscription. The proper way to solve this is to have the items be an Observable. You then make it depend on the outcome of the isUserLoggedInObservable of the auth service, but kick off the stream with the disabled state like so:

export class TopMenuComponent implements OnInit {
    constructor(private auth: AuthService) {}

    items$: Observable<MenuItem[]>;

    ngOnInit() {
        this.items$ = this.auth.isUserLoggedInObservable.pipe(
          map(isLoggedIn => getMenuItems(isLoggedIn)),
          startWith(this.getMenuItems(false))
        );
    }

    private getMenuItems(isLoggedIn: boolean): MenuItem[] {
      return [
        { label: 'Home', routerLink: '/' },
        { label: 'Tab1',  routerLink: '/tab1' },
        { label: 'Tab2',  routerLink: '/tab2', disabled: !isLoggedIn },
        { label: 'Tab3',  routerLink: '/tab3' },
      ];
    }
}

You can then use the items$ stream in the template with the async pipe:

<p-tabMenu [model]="items$ | async"></p-tabMenu>

Upvotes: 4

Related Questions