Fork me on GitHub

ngx-promise-buttons

Chilled Buttons for Angular

Star Fork Issue

npm version MIT license

ngx-promise-buttons is a simple Angular library that lets you add a loading indicator to a button of your choice. It is built as a standalone directive and works with modern Angular (tested up to Angular 22). Check out the demo!

Bug-reports or feature request as well as any other kind of feedback is highly welcome!

Why this package instead of angular2-promise-buttons?

This library is a maintained fork of angular2-promise-buttons. The original package stopped keeping pace with Angular’s platform changes, so apps on newer Angular versions hit friction: outdated peer/tooling support, NgModule-only setup (forRoot), and little ongoing maintenance for current releases.

Use ngx-promise-buttons when you need:

If you are still on a very old Angular / View Engine setup and already depend on angular2-promise-buttons, you can keep using that package. For Angular 13+ projects—especially standalone apps—prefer this one.

Getting started

Install it via npm:

npm install ngx-promise-buttons -S

Provide optional config in your application bootstrap:

import { bootstrapApplication } from '@angular/platform-browser';
import { provideNgxPromiseButtons } from 'ngx-promise-buttons';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
  providers: [
    provideNgxPromiseButtons({ handleCurrentBtnOnly: true }),
  ],
});

Import the directive in your standalone component and pass a promise (or boolean / subscription):

import { Component } from '@angular/core';
import { PromiseBtnDirective } from 'ngx-promise-buttons';

@Component({
  selector: 'app-example',
  imports: [PromiseBtnDirective],
  template: `
    <button (click)="someAction()"
       [promiseBtn]="promiseSetBySomeAction">Click me to spin!</button>
  `,
})
export class ExampleComponent {
  promiseSetBySomeAction: Promise<unknown>;

  // some example async action, but this works with any promise
  someAction() {
    this.promiseSetBySomeAction = new Promise((resolve) => {
      setTimeout(resolve, 2000);
    });
  }
}

Styling the button

To give you maximum flexibility there are no base styles coming with the directive, but it is easy to fix that! There are lots of free css-spinners out there. Just find one of your liking and add the css to your global stylesheet.

Resources:

There are selectors you can use to style. There is the .is-loading class on the button, which is set, when the promise is pending and there is the <span class="btn-spinner"></span> element inside the button.

Configuration

You can pass a config object to provideNgxPromiseButtons:

provideNgxPromiseButtons({
  // your custom config goes here
  spinnerTpl: '<span class="btn-spinner"></span>',
  // disable buttons when promise is pending
  disableBtn: true,
  // the class used to indicate a pending promise
  btnLoadingClass: 'is-loading',
  // only disable and show is-loading class for clicked button,
  // even when they share the same promise
  handleCurrentBtnOnly: false,
  // optional minimum time (ms) the spinner stays visible
  minDuration: null,
});

provideNgxPromiseButtons() is optional — omit it if the defaults are fine.

Using observables

When you're using the directive with observables make sure to pass a subscription to the directive rather than an observable directly.

const FAKE_FACTORY = {
  initObservable: (): Observable<number> => {
    return new Observable(observer => {
      setTimeout(() => {
        observer.complete();
      }, 4000);
    });
  }
};

// DO:
const observable = FAKE_FACTORY.initObservable();
this.passedToDirective = observable.subscribe(
// ...
);

// DON'T DO:
const observable = FAKE_FACTORY.initObservable();
this.passedToDirective = observable;

Using booleans

Is now also possible.

<button (click)="someAction()"
   [promiseBtn]="isShowBoolean">Click!</button>

Contributing

Contribution guidelines: CONTRIBUTING.md

Demo

Loading...