3

I want to add protection to components. If a user doesn't have permission to see this component it wont be rendered.

I have tried putting my if in the constructor and return false but it still renders.

I also added the if to the template itself but then I didn't see the view but the component is still alive and it adds complexity to the code as I need to maintain several places of the same if.

Is there a way to tell the component to not render at all ?

 constructor( private userService: UserService) {
    if (this.userService.isAllowed("see_trade_groups") === false) {
           return;
    }
}
MaxPowers
  • 5,235
  • 2
  • 44
  • 69
Amit Wagner
  • 3,134
  • 3
  • 19
  • 35

2 Answers2

3

For that purpose you can see CanActivate. Put it on the component route and it will do the job you want.

In that you can write a logic, based on which the route will be navigated or not.

Suren Srapyan
  • 66,568
  • 14
  • 114
  • 112
  • i need it on a component based and not a a route based as i can have many components on the same route – Amit Wagner Jan 22 '18 at 07:44
  • You have many components and want to show some and hide some based on condition ? – Suren Srapyan Jan 22 '18 at 07:45
  • if i put the ngif it works but the component still do api calls as only the template doesn't renders . then i need to add if on all the component hooks . something i rely want to avoid – Amit Wagner Jan 22 '18 at 07:48
  • The *ngIf should be **around** your component, not inside. If your component still make HTTP calls, it's either that, or you forgot to unsubscribe from your observables. –  Jan 22 '18 at 07:51
2

Component compilation lifecycle is handled by Angular compiler, so a component is unable to control its own lifecycle, and it should be controlled from the outside.

A common way to handle this is to use router. The lifecycle in route components differs from regular components because it's handled by router; they are attached to <router-outlet> component. It's possible to prevent compilation in route components but not in regular components.

Otherwise this should be handled with a directive. ngIf is built-in way to prevent the compilation of regular components.

So it becomes

<foo *ngIf="userService.isAllowed('see_trade_groups')"></foo>

Since this requires to inject userService to parent component every time it's needed, this will result in a lot of boilerplate code. An appropriate solution is to create a directive that behaves similarly to ngIf - or extend it to provide desired functionality:

import {Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
import {NgIf, NgIfContext} from '@angular/common';

...

@Directive({
    selector: '[role]'
})
class Role extends NgIf {
    @Input() role: string;

    constructor(
        viewContainer: ViewContainerRef,
        templateRef: TemplateRef<NgIfContext>
        public userService: User
    ) {
        super(viewContainer, templateRef);
    }

    ngOnChanges({ role }: SimpleChanges) {
        this.ngIf = this.userService.isAllowed(role);
        // can also subscribe to some observable to add/remove a component any time 
    }
}

Which is used like:

<foo *role="'see_trade_groups'"></foo>

Notice that Role is * structural directive. This allows it to control the compilation of an element it was specified on, similarly to how ngIf and ngFor do.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565