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

2

5

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.

Boughten answered 16/3, 2016 at 16:14 Comment(2)
I don't understand what is check access?Cene
I mean "check access" whether the visitor is entitled to a link - resource defined in data: {'resource':'account', 'privilage':'show'}Boughten
O
3

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);
  }

  (...)
}
Olen answered 16/3, 2016 at 18:8 Comment(8)
I like this, and see I was realy close to your solution, pls just tell me how can I make no-renderer this element (I don't wanna just hidden on the css style - my mistake with post question I mean hidden = no-renderer)Boughten
Yes, you're definitely right! Using @HostBinding for this is a better solution ;-) I updated my answer and the plunkr accordingly...Olen
Still have some problem with that, bcs find 2 bugs: - [first] the link not hiding - [second] error TS2341: Property '_navigationInstruction' is private and only accessible within class 'RouterLink' (don't know why this work in plunker)Boughten
In fact, it's because plunkr uses transpiling and not compilation so you can call private methods but it's not really fine... When you try to compile your application, it's normal to have errors. In fact, I think that the best approach would be to provide a patch angular2 itself. This way you could have access to the routeData from the RouterLink...Olen
Something like: this.routerLink.getRouteData();Olen
I updated my first post with question bcs have problem with multi level menu and access to routerLink my extend data section: Second versionBoughten
any ideia how to implement this in the latest angular version?Jubilate
@ThierryTemplier can you point me a solution example for ABAC (Attribute Based Access Control) in Angular 2 Type script. I want to set user menus with attribute based permissions. some menus will be hidden. and some are shown with restricted sectionsAcklin
C
0

when route becomes active in angular2, it by default adds .router-link-active class to the link.

So I'll hide or disable actived route by this,

@Component({
  selector: 'my-app',

  // to hide it,

  styles: [".router-link-active { Display: none;}"], //just add it in single line...

  // to hide it,

  styles: [".router-link-active { pointer-events: none;cursor: default;opacity: 0.6; }"], //just add it in single line...

  template:'...'
)}
Cene answered 16/3, 2016 at 16:46 Comment(2)
is this what you need?Cene
Thank's for your answer but, I want hide-remove link if visitior don't have ACL access to reasource (link). No hide active link like u say.Boughten

© 2022 - 2024 — McMap. All rights reserved.