How to conditionally insert/remove host DOM element in angular 2 directive

18,638

Solution 1

This implementation will be similar to what ngIf directive is. And the Angular guide for structural directives (which you plan to create too) gives an example of myUnless that just reverses ngIf.

You implementation for access will look similar to the myUnless implementation.

@Directive({ selector: '[access]' })
export class AccessDirective {
  constructor(
    private _templateRef: TemplateRef,
    private _viewContainer: ViewContainerRef
  ) { }
  @Input() set myUnless(condition: boolean) {
    if (condition) {
      this._viewContainer.createEmbeddedView(this._templateRef);
    } else {
      this._viewContainer.clear();
    }
  }
}

And use it like:

<li *access="isAdmin"><a>admin-only link</a></li>

Solution 2

Removing an element from the DOM is done with the *ngIf directive.

But if you really insist on using your own directive, I believe ElementRef is the way to go. Even though I would not advice on using this

You will get something like this:

import {Directive} from 'angular2/angular2';

@Directive({
  selector: '[access]' //this is how you address an attribute directive
})
export class Access {
  constructor(private _userService : UserService, private _elementRef : ElementRef) {}

  ngOnInit() {
    this._checkAdmin();
  }   

  private _checkAdmin() : void {
    if(!this._userService.currentUser.hasRole('admin')) {
      let el : HTMLElement = this._elementRef.nativeElement;
      el.parentNode.removeChild(el);
    }
  }
}
Share:
18,638
dKab
Author by

dKab

Updated on July 20, 2022

Comments

  • dKab
    dKab over 1 year

    I want to create a directive that will decide whether should or should not its host element apppear on the page. Ideally I would like it to remove the element from the DOM and not just hide/show it with css. Usecase is:

    <ul role="navigation">
      <li><a>public link</a></li>
      <li><a>public link2</a></li>
      <li access="admin"><a>admin-only link</a></li>
    </ul>
    

    It would use UserService to get currentUser roles and if there's no admin there the li would be removed.

    I suppose I could achieve the same effect with ng-if (if it's still available in angular 2) by passing the expression to evaulate in the main component. But with the directive it is more semantic and elegant.

    Is it possible?

    import {Directive} from 'angular2/angular2';
    
    @Directive({
        selector: 'access'
    })
    export class Access {
     //what goes here
    }
    

    I could've easily done that in angular 1 (inside directive's compile function), but how can I do this in Angular 2?

  • dKab
    dKab over 8 years
    why do you advise not to do this?
  • Poul Kruijt
    Poul Kruijt over 8 years
    because, in such a simple thing as this, *ngIf is way more intuitive, and leaves the DOM editing to angular
  • dKab
    dKab over 8 years
    I need to access value of the access attribute but in the constuructor it is not yet available - it's undefined
  • Poul Kruijt
    Poul Kruijt over 8 years
    I've edit my answer. I believe you have to use the ngOnInit function. This is called after the constructor and after attribute binding has finished
  • Mark Rajcok
    Mark Rajcok over 8 years
    The Attribute Directive guide warns against direct DOM manipulation, e.g., use of removeChild(): "Manipulating the DOM directly is a practice we'd rather avoid because it chains us to the browser DOM API... The rendering phase could be offloaded to a Web Worker for faster performance. Our directive might work when we ran the application outside the browser, perhaps on the server in a pre-render phase."
  • Glenn Bullock
    Glenn Bullock almost 7 years
    Had this same question and found this in the angular docs. angular.io/docs/ts/latest/guide/structural-directives.html