Stuart
Stuart

Reputation: 1612

Converting an Angular 1.x directive to Angular 2

I have a wrap-bootstrap template with a few widgets that I'm trying to convert to Angular 2. This one has me stumped:

.directive('changeLayout', function(){

    return {
        restrict: 'A',
        scope: {
            changeLayout: '='
        },

        link: function(scope, element, attr) {

            //Default State
            if(scope.changeLayout === '1') {
                element.prop('checked', true);
            }

            //Change State
            element.on('change', function(){
                if(element.is(':checked')) {
                    localStorage.setItem('ma-layout-status', 1);
                    scope.$apply(function(){
                        scope.changeLayout = '1';
                    })
                }
                else {
                    localStorage.setItem('ma-layout-status', 0);
                    scope.$apply(function(){
                        scope.changeLayout = '0';
                    })
                }
            })
        }
    }
})

I have a new directive started (below), but the is and prop properties of the HtmlElement object don't actually exist. How can I get/set the checked property of the input element on which I drop this attribute? I'm also thinking that this directive exposes the layout status through the Output attribute so the parent component can respond accordingly.

Can anyone guide me a bit? Am I on the right track? Thanks!

New directive:

import { Directive, ElementRef, Output, EventEmitter, OnInit } from '@angular/core';

import { LocalStorage } from "../common";

@Directive({
    selector: '[change-layout]',
    host: {
        '(change)': 'onChange()'
    }
})

export class ChangeLayoutDirective implements OnInit {

    private el: HTMLElement;
    @LocalStorage() public layoutStatus: Number = 0;

    @Output() layoutStatusChange = new EventEmitter();

    constructor(el: ElementRef) {
        this.el = el.nativeElement;
    }

    ngOnInit() {
        this.el.prop('checked', false);
        if(this.layoutStatus === 1) {
            this.el.prop('checked', true);
        }
    }

    onChange() { this.toggleLayout(); }

    private toggleLayout() {
        if(this.el.is(':checked')) {
            this.layoutStatus = 1;
        }
        else {
            this.layoutStatus = 0;
        }
        this.layoutStatusChange.emit({
            value: this.layoutStatus
        });
    }
}

Upvotes: 2

Views: 3528

Answers (1)

Mark Rajcok
Mark Rajcok

Reputation: 364747

I suggest using HostBinding and HostListener to set up two-way databinding to the checked property of your host element. This way, you don't need to use ElementRef or nativeElement.

export class ChangeLayoutDirective implements OnInit {
  private layoutStatus = 0;
  @Output() layoutStatusChange = new EventEmitter();
  @HostBinding('checked') checked = false;
  @HostListener('change', ['$event.target.checked'])
  onChange(checked) { 
    console.log('checked =', checked);
    this.toggleLayout(checked); 
  }
  ngOnInit() {
    if(this.layoutStatus === 1) {
      this.checked = true;
    }
  }
  private toggleLayout(checked) {
    this.layoutStatus = checked ? 1 : 0;
    this.checked = checked;
    this.layoutStatusChange.emit({
      value: this.layoutStatus
    });
  }
}

Plunker

HostBinding sets up property binding between your directive's checked property and the checked property of the host/DOM element.

HostListener sets up event binding between your directive's onChange() method and the change event of the host/DOM element.

Using an Output property to inform the parent of changes is a good approach.

Upvotes: 2

Related Questions