Before implementing a solution on my own I would like to know if there is a simple manner to change the style of an element (a brief highlight) when the data bound property value has just change.
There are lot of elements in my DOM so I do not not want to store and maintain a dedicated property in component.
My elements to highlight are traditional input form's elements:
<tr field label="Lieu dépôt">
<select class="cellinput" #lieuDepotBon [(ngModel)]="rapport.LieuDepotBon" (ngModelChange)="changeRapport({LieuDepotBon:$event})">
<option [ngValue]="null"></option>
<option [ngValue]="i" *ngFor="let depotBonChoice of DepotBonInterventionValues; let i = index">{{DepotBonIntervention[i]}}</option>
</select>
</tr>
<tr field *ngIf="rapport.LieuDepotBon==DepotBonIntervention.Autre" label="Autre lieu">
<input class="cellinput" #autreLieuDepotBon [(ngModel)]="rapport.AutreLieuDepotBon" (ngModelChange)="changeRapport({AutreLieuDepotBon:autreLieuDepotBon.value})" />
</tr>
I heard about special class styles set by Angular2 on element with ngModel directive that could help do what I need but I could not find more about it.
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 {};
}