Jorge Iten Jr
Jorge Iten Jr

Reputation: 362

Create a reusable "Loading Button" directive in Angular2

Basically I want the same behavior described here but working in a directive, since I am going to use throughout my app.

So far I got this in my directive:

@Directive({
  selector: '[asyncLoader]'
})
export class ActionAsyncLoader {

  @Input('asyncLoader') asyncLoader: string;
  ...

  //1 - save the text for further use.
  ngOnInit(){
    this.text = this.elementRef.nativeElement.innerHTML;

  } 
  //2 - change the text when "click" is triggered
  @HostListener('click', ['$event.target']) onClick(btn) {
    btn.innerHTML = 'Loading';
  }
  //3 - change text back to the normal value
  onCallbackAsync(obj){
      this.elementRef.nativeElement.innerHTML = this.text;
  }
}

Steps 1 and 2 are currently working fine, my problem is in the step 3. Where Can I bind my function to the end of event executed on click function(Usually http requests)?

Upvotes: 2

Views: 2501

Answers (2)

Menzo
Menzo

Reputation: 1

Just want to share with you and others my way to solve current "challenge"

@Directive({
  selector: '[appSpinner]'
})
export class SpinnerDirective implements OnInit, OnChanges {
  @Input() loadingState: boolean;
  contentText;

  constructor(private elem: ElementRef) {
    if (this.elem.nativeElement.innerText !== 'undefined') {
      this.contentText = this.elem.nativeElement.innerText;
    }
  }

  ngOnInit(): void {
    if (this.loadingState) {
      this.elem.nativeElement.innerHTML = '<div class="spinner-border custom-spinner-btn"></div>';
    }
  }

  ngOnChanges(changes): `void` {
    this.loadingState = changes.loadingState?.currentValue;
    if (this.loadingState) {
      this.elem.nativeElement.innerHTML = '<div class="spinner-border custom-spinner-btn"></div>';
    } else {
      if (this.contentText) {
        this.elem.nativeElement.innerText = this.contentText;
      }
    }
  }
}

HTML:

<button
      appSpinner
      [loadingState]="true"
      type="submit"
      class="btn btn-primary">
      LOGIN
    </button>

Here is example where I get idea of it.

Upvotes: 0

Joshua Ohana
Joshua Ohana

Reputation: 6121

You've got two good options: 1) use an input EventEmitter 2) access case #3 via a shared singleton service.

1) would probably be best if the component using the directive has direct knowledge on when to change the text back, you'd do something like...

// in your directive
@Input() changeText: EventEmitter<any>;
ngOnInit() {
    this.changeText.subscribe(event => {
        // do stuff
    });
}

// in your html
<div [asyncloader] changeText="myEventEmitter">

// and in your component
public myEventEmitter = new EventEmitter();
myEventEmitter.emit("change some text please");

The second option would work well if other components might want to interact with the loader in question in a more global nature. You'd basically do the same thing as in the first in the directive, except expose your EventEmitter from an injectable service. Then other services, components, whatever, could request for that service to broadcast, and your directive would know what's going on when to change.

Upvotes: 1

Related Questions