Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2. How to hide(no-render) the link in the menu after check access?

Need to hide link in menu based on the "routerLink" after check ACL access. I do not want to use angular directives "*ngIf" on each element link in app (need to do that globaly on the routerConfig definition)

Content can controll using annotation @CanActivate on components but need to hide link in menu

I try do that with overwrite "routerLink" directive, but in this directives after overwrite can't get access to my extends param defined(resources and privilages) in routerConfig

Example:

@Component({})
@RouteConfig([{   
    path: '/dashboard', 
    name: 'Dashboard', 
    component: DashboardComponent, 
    data: {'resource':'account', 'privilage':'show'} 
}])

But can't get access to this config data(routerData) in the "routerLink".

Some idea how can do that?

Second version

Multi level menu and stiil have problem with get access to the (extend data config) from routerConfig definition.

Main component

@RouteConfig([
    { path:'/dashboard',   name: 'DashboardLink',   component: DashboardComponent,  data: { res: 'dasboard', priv: 'show'}, useAsDefault: true },
    { path:'/user/...',    name: 'UserLink',        component: UserComponent,       data: { res: 'user', priv: 'show'} },
 ])

@Component({
    selector: 'main-app',
    template: `
            <ul class="left-menu">
                <li><a secured [routerLink]="['UserLink']">User</a>
                    <ul>
                        <li><a secured [routerLink]="['ProfileLink']">Profile</a></li>
                    </ul>
                </li>
                <li><a secured [routerLink]="['HomeLink']">Home</a></li>
                <li><a secured [routerLink]="['DashboardLink']">Dashboard</a></li>
            </ul>

            <router-outlet></router-outlet>
    `
})
export class MainComponent {

}

User component

@RouteConfig([
    { path: '/profile', name: 'ProfileLink', component: ProfileComponent, data: {res: 'user', priv: 'details'} },
])
@Component({ ... })
export class UserComponent {
}

Profile component

@Component({ ... })
export class ProfileComponent {

}

My secure directives

@Directive({
    selector: '[secured]'
})
export class SecuredDirective {

    @Input('routerLink')
    routeParams: any[];

    /**
     * 
     */
    constructor(private _viewContainer: ViewContainerRef, private _elementRef: ElementRef, private router:Router) {
    }

    ngAfterViewInit(){
        //Get access to the directives element router instructions (with parent instructions)
        let instruction = this.router.generate(this.routeParams);
            //Find last child element od instruction - I thing thats my component but I am not sure (work good with two levels of menu)
            this.getRouterChild(instruction);
    }

    private getRouterChild(obj: any){
        var obj1 = obj;
        while(true) {
            if( typeof obj1.child !== 'undefined' && obj1.child !== null ){
                obj1 = obj1.child;
            } else {
                break;
            }  
        }
        this.checkResPrivAcl(obj1.component.routeData.data)
    }
    private checkResPrivAcl(aclResAndPriv: any){

        let hasAccess = CommonAclService.getInstance().hasAccess(aclResAndPriv['res'], aclResAndPriv['priv']);

        if (!hasAccess) {
            let el : HTMLElement = this._elementRef.nativeElement;
            el.parentNode.removeChild(el);   
        }

        console.log("CHECK ACL: " + aclResAndPriv['res'] + " | " + aclResAndPriv['priv']);
    }
}

This solution work only for child item of menu don't work with the main level of menu, and not sure is this work correctly on the multi level menu.

I try diffrent way to resolve this problem I try get access to the RouterConfig definition (and my routerData extend config) in the directives and check element by the alias name of link using @Input('routerLink') but don't find how can I get access to the RouterConfig.

like image 862
Power Web Design Avatar asked Oct 18 '22 13:10

Power Web Design


1 Answers

I think that you could create a dedicated directive to do that. This directive would be attach on the same HTML element that contains the routerLink one. This way you would have access to both native element and RouterLink directive:

@Directive({
  selector: '[secured]'
})
export class Secured {
  constructor(private routerLink:RouterLink,private eltRef:ElementRef) {
    (...)
  }
}

and use it this way:

@Component({
  selector: 'app',
  template: `
    <router-outlet></router-outlet>
    <a secured [routerLink]="['./Home']">Home</a>
  `,
  providers: [ ROUTER_PROVIDERS ],
  directives: [ ROUTER_DIRECTIVES, Secured ],
  pipes: []
})
@RouteConfig([
  {
    path: '/home',
    name: 'Home',
    component: HomeComponent,
    useAsDefault: true,
    data: {'resources':'account', 'privilages':'show'} 
  },
  (...)
])
export class ...

Based on the RouterLink directive instance, you can have access to the data you specified when defining the route and hide the element if necessary:

export class Secured {
  @HostBinding('hidden')
  hideRouterLink:boolean;

  constructor(private routerLink:RouterLink) {
  }

  ngAfterViewInit()
    var data = this.routerLink._navigationInstruction.component.routeData.data;
    this.hideRouterLink = this.shouldBeHidden(data);
  }

  (...)
}

See this plunkr in the src/app.ts

Edit

As suggested in the issue by Brandon, we could regenerate the component instruction from the value specified in the routerLink attribute:

export class Secured {
  @HostBinding('hidden')
  hideRouterLink:boolean;

  @Input('routerLink')
  routeParams:string;

  constructor(private router:Router) {
  }

  ngAfterViewInit() {
    var instruction = this.router.generate(this.routeParams);
    var data = instruction.component.routeData.data;
    this.hideRouterLink = this.shouldBeHidden(data);
  }

  (...)
}
like image 152
Thierry Templier Avatar answered Oct 21 '22 04:10

Thierry Templier