Angular: How to temporarily highlight dom elements that have just changed?
The easiest and cleaner way I can think of is to implement 2 css classes like so:
.highlight{ background-color: #FF0;}.kill-highlight{ background-color: #AD310B; -webkit-transition: background-color 1000ms linear; -ms-transition: background-color 1000ms linear; transition: background-color 1000ms linear;}
and then affect both of them successively to the element.hope that helps
Here is my solution.
I wanted to highlight the datas in the Form that are changed by other users in real-time.
In my HTML form, I replaced native html elements by Angular components. For each type of native element I created a new Angular Component with Highlight support. Each component implements the ControlValueAccessor Angular interface.
In the parent form I replaced the native element:
<input [(ngModel)]="itinerary.DetailWeather" />
by my custom element:
<reactive-input [(ngModel)]="itinerary.DetailWeather"></reactive-input>
When Angular calls detectChanges() for the parent form, it does check all the datas that are used as inputs by the components of the form.
If a component is a ControlValueAccessor, and a change occurred in the application model, it does call the method ControlValueAccessor.writeValue( value ). It is the method that is called when the data changed in memory. I use it as a hook to update temporarily the style to add the highlight.
Here is the custom element. I used Angular Animations for updating the border color and fade back to the original color.
import { Component, Input, forwardRef, ChangeDetectorRef } from '@angular/core';import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';import { trigger, state, style, animate, transition, keyframes } from '@angular/animations';@Component({ selector: 'reactive-input', template: `<input class="cellinput" [(ngModel)]="value" [@updatingTrigger]="updatingState" />`, styles: [`.cellinput { padding: 4px }`], animations: [ trigger( 'updatingTrigger', [ transition('* => otherWriting', animate(1000, keyframes([ style ({ 'border-color' : 'var( --change-detect-color )', offset: 0 }), style ({ 'border-color' : 'var( --main-color )', offset: 1 }) ]))) ]) ], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReactiveInputComponent), multi: true } ]})export class ReactiveInputComponent implements ControlValueAccessor { public updatingState : string = null; _value = ''; // stores the action in the attribute (onModelChange) in the html template: propagateChange:any = ( change ) => {}; constructor( private ref: ChangeDetectorRef ) { } // change from the model writeValue(value: any): void { this._value = value; this.updatingState = 'otherWriting'; window.setTimeout( () => { this.updatingState = null; }, 100 ); // model value has change so changes must be detected (case ChangeDetectorStrategy is OnPush) this.ref.detectChanges(); } // change from the UI set value(event: any) { this._value = event; this.propagateChange(event); this.updatingState = null; } get value() { return this._value; } registerOnChange(fn: any): void { this.propagateChange = fn; } registerOnTouched(fn: () => void): void {} setDisabledState?(isDisabled: boolean): void {};}
To highlight DOM element for a moment, use setTimeout()
to add or remove CSS class
check demo
HTML
<mat-form-field [ngClass]="{'changed': isChanged}"> <mat-select [(ngModel)]="yourModel" (ngModelChange)="modelChanged($event)"> <mat-option value="1">1</mat-option> <mat-option value="2">2</mat-option> <mat-option value="3">3</mat-option> </mat-select></mat-form-field>
Typescript
isChanged: boolean = falsemodelChanged(value) { console.log('model changed') this.isChanged = true setTimeout(() => { this.isChanged = false }, 1000);}
CSS
.changed { transition: color 0.4s ease-in, text-shadow 0.4s ease-in, background-color 0.5s linear 0s; text-shadow: #bbb 2px 2px 2px; background-color: #ffffcc; color: #BF1722;}
Note: if your application changes in milliseconds then you should reduce the time for setTimeout()
to 0.5s or 0.3s as per your application requirement.
Thanks to Ingo Bürk for pointing-out this issue