Components: Add spinner to mat-button

Created on 9 May 2019  Â·  13Comments  Â·  Source: angular/components

Please describe the feature you would like to request.

mat-button clicks often involve an asynchronous operation. Adding a progress spinner or some sort of 'waiting' function makes sense. Disabling the button isn't good enough as it can appear broken to a user.

What is the use-case or motivation for this proposal?

Sometimes async operations can involve a few seconds or more as the server is doing something. Users need a visual cue that they should be waiting.

P4 materiabutton materiaprogress-spinner feature

Most helpful comment

I don't suspect this will be on the roadmap for a while, but in the meantime you should be able to set this up by overlaying the button with a spinner like @andreElrico suggested

Here's an example of this: https://stackblitz.com/edit/angular-k5a64z?file=app/button-overview-example.ts

All 13 comments

Cant you add a spinner into the button and disable clicking the button via css?

I don't suspect this will be on the roadmap for a while, but in the meantime you should be able to set this up by overlaying the button with a spinner like @andreElrico suggested

Here's an example of this: https://stackblitz.com/edit/angular-k5a64z?file=app/button-overview-example.ts

Shame this won't be done for a while - especially since it's part of the material spec.

This project does it very nicely, but 'native' support would be great.

https://github.com/michaeldoye/mat-progress-buttons
Demo: https://mat-progress-buttons.firebaseapp.com/home

For simple 'Save' functionality these are a very nice touch.

Shame this won't be done for a while - especially since it's part of the material spec.

This project does it very nicely, but 'native' support would be great.

https://github.com/michaeldoye/mat-progress-buttons
Demo: https://mat-progress-buttons.firebaseapp.com/home

For simple 'Save' functionality these are a very nice touch.

It does not support static i18n tags, and it has a lot of open issues still.

This MUST be supported natively, as a state of a button.

@simeyla why should the Material team waste time when you can simply combine the two components yourself?
There is so much important stuff to do.

The fact its mentioned in the specs does not mean it needs to be implemented by the mat-team. Its a guide how things should look and feel. Everything the mat-team builds should meet the specs requirements.

@AndreEllrich I'm not insisting on anything. I just said it was a shame. But in answer to your question

  • For the same reason I use any library control! And because it's never as simple as just combining two controls
  • Because I did the minimum to get it to work!
  • They'd do a much better job than I could in handling things like:

    • obscure browser / device issues

    • accessibility

    • animation timing and thresholds

    • making it with a directive vs. a component (my version below is a component)

    • current and future compatibility with other button directives

    • making it just feel 'right'

As I said a spinner button is a very useful control to have and wouldn't take a huge amount of time for an expert to create.

Anyway here's the version I ended up with - if it helps anyone then great :-)

  <rr-spinner-button [isBusy]="isSaving" buttonStyle="flat" 
                     (click)="saveChanges()">Save</rr-spinner-button>
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core';

@Component({
    selector: 'rr-spinner-button',

    template: `<button mat-button 

                    [color]="color"
                    
                    [class.mat-flat-button]="buttonStyle == 'flat'"
                    [class.mat-raised-button]="buttonStyle == 'raised'"
                    [class.mat-stroked-button]="buttonStyle == 'stroked'"

                    [class.busy]="isBusy"
                    [disabled]="isDisabled"
                    [type]="type">

                    <div class="content" [class.busy]="isBusy">
                        <ng-content></ng-content>
                    </div>

                    <mat-spinner [mode]="'indeterminate'" 
                                 [diameter]="19" 
                                 [class.busy]="isBusy"></mat-spinner>
               </button>

               `,

    styles: [`  :host 
                {
                    position: relative;
                }

                :host.disabled
                {
                    // outline: 1px solid red;
                    pointer-events: none;
                }

                mat-spinner 
                { 
                    position: absolute;
                    top: 25%;

                    opacity: 0;

                    transition: opacity .3s ease-in-out;
                }

                mat-spinner.busy 
                {
                    opacity: 1;
                }

                .content 
                {
                    opacity: 1;
                    transition: opacity .3s ease-in-out;
                }

                .content.busy 
                {
                    opacity: 0;
                }

                button ::ng-deep .mat-button-wrapper
                {
                    display: flex;
                    align-items: center;
                    justify-content: center;
                }
             `
            ],

    changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpinnerButtonComponent {

    // inspired by https://www.npmjs.com/package/mat-progress-buttons
    constructor() { }

    @Input("isBusy")
    isBusy = false;

    @Input("disabled")
    disabled = false;

    @Input("color")
    color!: string;

    @Input("type")
    type: 'button' | 'submit' = 'button';

    @Input('buttonStyle')
    buttonStyle: 'flat' | 'raised' | 'stroked' = 'flat';

    @HostBinding('class.disabled')
    get isDisabled() 
    {
        return this.isBusy || this.disabled;
    }
}

```

Andre,

This is not wasting time, angular components is a bootstrap for applications, and by the 2k downloads weekly that the rogue implementation has you can see there is demand for it.

They also covered this scenario in the material specs because it is a very common use for async activities.

Combining both components is not enough, there is state management, transition, animation. It's alot of hardwork for something that should come out of the box.

Yours,

via Newton Mail [https://cloudmagic.com/k/d/mailapp?ct=pa&cv=10.0.25&pv=9&source=email_footer_2] On Fri, Jan 17, 2020 at 6:45am, AndreEllrich < [email protected] [[email protected]] > wrote:
@simeyla [https://github.com/simeyla] why should the Material team waste time when you can simply combine the two components yourself?
There is so much important stuff to do.

The fact its mentioned in the specs does not mean it needs to be implemented by the mat-team. Its a guide how things should look and feel. Everything the mat-team builds should meet the specs requirements.

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub [https://github.com/angular/components/issues/15982?email_source=notifications&email_token=AAU4DNIOH7DQ35BAQ2YPSRTQ6FAYVA5CNFSM4HLYO72KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJGQWSI#issuecomment-575474505] , or unsubscribe [https://github.com/notifications/unsubscribe-auth/AAU4DNNVIGATLQL327IJCITQ6FAYVANCNFSM4HLYO72A] .

Hey Simeyla,

nice try: However you are reinventing the wheels here! (which means reimplemting the mat-button api.)
I used a compound component style:

https://stackblitz.com/edit/angular-k5a64z-bfbkuw?file=app%2Fbutton-overview-example.html

<app-mat-spinner-btn [loading]="loading">
  <button mat-raised-button (click)="'Something'">Click me!</button>  
</app-mat-spinner-btn>

please note:

  • this is angular7 in 8+ you will have to tweak a few things. I think the material import need to be from material/button i believe
  • css can be improved, see if you can avoid !important ... but I thinks its okay here!

Angular 8.2.9, with some mild css animation

https://stackblitz.com/edit/angular-9vfbuy?file=src%2Fstyles.scss

You proved my point which is that the one in the spec looks better than
both of our attempts :)

I did not want a spinner that just faded out the button to grey. I needed
the button contents to disappear so that’s why I choose the way I did it.
Fastest way I could because yes better things to do!

Anyway still hoping one day they’ll add it and save everyone some time.

On Fri, Jan 17, 2020 at 02:14 AndreEllrich notifications@github.com wrote:

Hey Simeyla,

nice try: However you are reinventing the wheels here! (which means
reimplemting the mat-button api.)
I used a compound component style:

https://stackblitz.com/edit/angular-k5a64z-bfbkuw?file=app%2Fbutton-overview-example.html



please note:

  • this is angular7 in 8+ you will have to tweak a few things. I think
    the material import need to be from material/button i believe
  • css can be improved, see if you can avoid important ... but I thinks
    its okay here!

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/angular/components/issues/15982?email_source=notifications&email_token=AA34UVTZFZ6Y5V3VEETL4RDQ6GAJNA5CNFSM4HLYO72KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJHGLLY#issuecomment-575563183,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AA34UVXFQQRODDC5BFFTEL3Q6GAJNANCNFSM4HLYO72A
.

This can be done very easily with css. If i have time ill update. To be honest I did not look at the specs :D

Hi, I found a simple trick

      <button *ngIf="!this.SpinerC2" class="space-boton" mat-raised-button color="warn">
        Update Tickets<mat-icon><mat-progress-spinner diameter="20" mode="indeterminate" strokeWidth="11%"> 
        </mat-progress-spinner></mat-icon>
      </button>

Put your mat-progress-spiner in a mat-icon tag. it works for me!

Agree this would be a super-useful addition. I'm having trouble figuring out how to implement something like this. Must be a very common scenario. @DavidAlfonsoo suggestion works but don't want to hardcode a diameter. A couple other solutions here involve hardwiring the spinner diameter which is not a good idea. I may end up using the bar progress instead simply because I can dock it to the top of the panel.

Duplicate of #13667

Was this page helpful?
0 / 5 - 0 ratings

Related issues

theunreal picture theunreal  Â·  3Comments

vanor89 picture vanor89  Â·  3Comments

crutchcorn picture crutchcorn  Â·  3Comments

MurhafSousli picture MurhafSousli  Â·  3Comments

kara picture kara  Â·  3Comments