Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change angular material progress bar color from code

I need to change the color of angular material progress bar dynamically. I realized that it is not that simple, so what is the best way to to it?

requirements:

  1. I will receive the color hex code from an external API. So I can not create a set of predefined themes
  2. The background color will be white. So, I just need one color, not the entire palette (lighter, darker) colors.

related link: (1)

like image 590
guilhermecgs Avatar asked Sep 14 '18 10:09

guilhermecgs


People also ask

Can't bind to color since it isn't a known property of Mat progress-bar?

If 'mat-progress-bar' is an Angular component and it has 'color' input, then verify that it is part of this module. If 'mat-progress-bar' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule. schemas' of this component to suppress this message.

How do I create a progress-bar in HTML?

Use the <progress> tag to create a progress bar in HTML. The HTML <progress> tag specifies a completion progress of a task. It is displayed as a progress bar. The value of progress bar can be manipulated by JavaScript.


2 Answers

We can create an attribute directive which accepts the color value and override default styles of <mat-progress-bar> for us.

here is a working demo : https://stackblitz.com/edit/material-progress-bar-color-directive

here is a brief explanation:

If we inspect <mat-progress-bar> in developer tools. we will find that color of the progress-bar is defined in the ::after pseudo element like this.

.mat-progress-bar-fill::after {
    background-color: #3f51b5;
}

And as we already know that it is not possible to directly manipulate a pseudo element using DOM querySelector() method. But we can add new styles which can have rules for pseudo elements too. checkout this thread for more details. https://stackoverflow.com/a/21709814/1160236

So, we can make a directive which can take care of adding new styles for us.

import { Directive, Input, OnChanges, SimpleChanges, ElementRef } from '@angular/core';

@Directive({
  selector: '[appProgressBarColor]'
})
export class ProgressBarColor implements OnChanges{
  static counter = 0;

  @Input() appProgressBarColor;
  styleEl:HTMLStyleElement = document.createElement('style');

  //generate unique attribule which we will use to minimise the scope of our dynamic style 
  uniqueAttr = `app-progress-bar-color-${ProgressBarColor.counter++}`;

  constructor(private el: ElementRef) { 
    const nativeEl: HTMLElement = this.el.nativeElement;
    nativeEl.setAttribute(this.uniqueAttr,'');
    nativeEl.appendChild(this.styleEl);
  }

  ngOnChanges(changes: SimpleChanges): void{
    this.updateColor();
  }

  updateColor(): void{
    // update dynamic style with the uniqueAttr
    this.styleEl.innerText = `
      [${this.uniqueAttr}] .mat-progress-bar-fill::after {
        background-color: ${this.appProgressBarColor};
      }
    `;
  }

}

as you can see that all that we are doing here is just making a new HtmlStyleElement and adding it just inside the host element.

And inside updateColor() method we are updating the innerText of the style tag we have appended. notice that we are using an attribute selector here with a unique attribute to minimize the scope of the style to the host only. because we want to override the style only for that progress-bar on which we have applied our directive.

you can use this directive in your template like this.

<mat-progress-bar [appProgressBarColor]="'orange'"
                  mode="determinate" 
                  value="40"></mat-progress-bar>

I hope this will help.

like image 52
HirenParekh Avatar answered Sep 19 '22 07:09

HirenParekh


Update:

Avoid using deep, TL;DR: Deep is technically invalid (like, deeprecated :p)

For more info refer: The use of /deep/ and >>> in Angular 2

Now, to change the color of mat-progress bar,

Head over to your styles.scss file (or the main css/scss file in your project)

Add this class -->

.green-progress .mat-progress-bar-fill::after {
    background-color: green !important;
}

Your mat-progress should use the above class, like -->

<mat-progress-bar class="green-progress" mode="indeterminate"></mat-progress-bar>
like image 39
Meet Dave Avatar answered Sep 19 '22 07:09

Meet Dave