Angular – Changing CSS with ElementRef
Overview
This is a tutorial for building a horizontal meter with 3 fill color values.

Each of the values on the inside can be modified dynamically with typescript using ViewChild() and ElementRef. With ElementRef you can modify properties of a CSS selector. In this example we will modify the width but you can also modify properties such as height, color, margin, padding, transform etc.
Create Component
First generate a new component called average-meter.
ng generate component average-meter
You should see the following files:

Build the meter with HTML and CSS.
HTML average-meter.component.html
<div class="bar">
<div class="low-end">
<p>20%</p>
</div>
<div class="avg-mid">
<p>20%</p>
</div>
<div class="high-end">
<p>20%</p>
</div>
</div>
CSS
average-meter.component.scss or average-meter.component.css
.bar {
width: 100%;
height: 30px;
border-radius: 10px;
background-color: #b3b3b3;
color: white;
text-align: center;
}
.low-end {
width: 33%;
height: 100%;
background-color: green;
float: left;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
.avg-mid {
width: 33%;
height: 100%;
background-color: orange;
float: left;
}
.high-end {
width: 33%;
height: 100%;
background-color: red;
float: left;
}
You should see the bar in Figure 1.

Next set up the TypeScript file: average-meter.component.ts
Import the following:
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, Input } from '@angular/core';
Add the Inputs since we want to make this component reusable. Also add the Element References.
@Input() lowEndVal: number;
@Input() midAvgVal: number;
@Input() highEndVal: number;
@ViewChild('lowEnd') lowEnd: ElementRef;
@ViewChild('avgMid') avgMid: ElementRef;
@ViewChild('highEnd') highEnd: ElementRef;
We will have to use AfterViewInit since we want to make the change to an HTML element that has already been rendered.
export class AverateMeterComponent implements OnInit, AfterViewInit {
The ngAfterInit() the logic will look like the following:
ngAfterViewInit() {
this.lowEndVal = 20;
this.midAvgVal = 20;
this.highEndVal = 20;
this.lowEnd.nativeElement.style.width = this.lowEndVal + '%';
this.avgMid.nativeElement.style.width = this.midAvgVal + '%';
this.highEnd.nativeElement.style.width = this.highEndVal + '%';
}
All together for the component TypeScript file:
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, Input } from '@angular/core';
@Component({
selector: 'app-averate-meter',
templateUrl: './total-averate-meter.component.html',
styleUrls: ['./total-averate-meter.component.scss']
})
export class AverateMeterComponent implements OnInit,
AfterViewInit {
@Input() lowEndVal: number;
@Input() midAvgVal: number;
@Input() highEndVal: number;
@ViewChild('lowEnd') lowEnd: ElementRef;
@ViewChild('avgMid') avgMid: ElementRef;
@ViewChild('highEnd') highEnd: ElementRef;
ngAfterViewInit() {
this.lowEnd.nativeElement.style.width = this.lowEndVal + '%';
this.avgMid.nativeElement.style.width = this.midAvgVal + '%';
this.highEnd.nativeElement.style.width = this.highEndVal + '%';
}
ngOnInit(): void {
// Hard coded values to test it
this.lowEndVal = 20;
this.midAvgVal = 20;
this.highEndVal = 20;
}
}
In the HTML template add the ViewChild Element Reference attributes.
<div class="low-end" #lowEnd>
<div class="avg-mid" #avgMid>
<div class="high-end" #highEnd>
<div class="bar">
<div class="low-end" #lowEnd>
<p>{{lowEndVal}}%</p>
</div>
<div class="avg-mid" #avgMid>
<p>{{midAvgVal}}%</p>
</div>
<div class="high-end" #highEnd>
<p>{{highEndVal}}%</p>
</div>
</div>
When you are ready to use this from another component just remove the hard coded values and pass them in as follows:
<app-average-meter [lowEndVal]="YOURLOWVALVAR"
[midAvgVal]="YOURMIDVALVAR"
[highEndVal]="YOURHIGHVALVAR" />
This next part is completely Optional:
To make your components a little nicer we can remove the app from app-average-meter in the average-meter.component.ts file by updating the selector under @Component.
Change selector: ‘app-average-meter’ to selector: ‘average-meter’,
@Component({
selector: 'average-meter',
templateUrl: './average-meter.component.html',
styleUrls: ['./average-meter.component.scss']
})
Now you can call it as
<average-meter [lowEndVal]="YOURLOWVALVAR"
[midAvgVal]="YOURMIDVALVAR"
[highEndVal]="YOURHIGHVALVAR" />
In addition to changing CSS elements, @ViewChild can change the entire class with nativeElement.className.
For example: A CSS file with two class definitions
.blue-font{
background-color: 'white';
color: 'darkblue';
}
.white-font{
background-color: 'darkblue';
color: 'white';
}
From the HTML file:
<span class="blue-font" #HeaderSpan>Hello World</span>
<button type="button" (click)="onChangeFont('blue')">Blue</button>
<button type="button" (click)="onChangeFont('blue')">Blue</button>
From the component typescript file:
import { Component, ViewChild, ElementRef } from '@angular/core';
....
....
@ViewChild('HeaderSpan') formCardElement: ElementRef;
....
....
public onChangeFont(color: string) {
if(color === 'blue') {
this.SpanElement.nativeElement.className = 'blue-font';
} else {
this.SpanElement.nativeElement.className = 'white-font';
}
}
You must be logged in to post a comment.