ETA: I know that there are various ways to watch my form for changes. That is not what I am trying to do. As the title says, I am asking how to watch for change
Angular usually uses injected into constructor KeyValueDiffers class.
For your case it could look like:
import { KeyValueChanges, KeyValueDiffer, KeyValueDiffers } from '@angular/core';
export class Customer {
firstName: string;
favoriteColor: string;
}
@Component({
selector: 'my-app',
templateUrl: `./app.component.html`
})
export class AppComponent {
private customerDiffer: KeyValueDiffer<string, any>;
private customer: Customer;
constructor(private differs: KeyValueDiffers) {}
ngOnInit(): void {
this.customer = new Customer();
this.customerDiffer = this.differs.find(this.customer).create();
}
customerChanged(changes: KeyValueChanges<string, any>) {
console.log('changes');
/* If you want to see details then use
changes.forEachRemovedItem((record) => ...);
changes.forEachAddedItem((record) => ...);
changes.forEachChangedItem((record) => ...);
*/
}
ngDoCheck(): void {
const changes = this.customerDiffer.diff(this.customer);
if (changes) {
this.customerChanged(changes);
}
}
}
Stackblitz Example
One more option is using setter on properties that you want to check.
See also
I need to subscribe directly to changes on the model.
Then you need to listen to model changes with ngModelChange
Template:
<input type="text" (ngModelChange)="doSomething($event)" [ngModel]="customer.firstName">
Class:
doSomething(event) {
console.log(event); // logs model value
}
DEMO
visit https://github.com/cartant/rxjs-observe. it bases on rxjs and proxy.
import { observe } from "rxjs-observe";
const instance = { name: "Alice" };
const { observables, proxy } = observe(instance);
observables.name.subscribe(value => console.log(name));
proxy.name = "Bob";
You can't watch changes in an object. Its not angular 1 there are no watchers here. Another solution will be via observables.
use form
<form #f="ngForm">
<input type="text" name="firstName" [(ngModel)]="customer.firstName">
<input type="text" name="favoriteColor" [(ngModel)]="customer.favoriteColor">
</form>
in code
@ViewChild('f') f;
ngAfterViewInit() {
this.f.form.valueChanges.subscribe((change) => {
console.log(change)
})
}
We are tasked with converting an Angular 1.x app to Angular 9. It's an application with ESRI maps, so we have some neat tools that the ESRI framework brought to the table. ESRI has watchUtils that do a whole lot more than just watch for changes.
But I missed Angular 1's simple $watch. Besides, we create Entities and Models in our application, and we may need to observe these from time to time.
I created an abstract class called MappedPropertyClass. It uses a Map<string, any> to map class properties, which allows me to easily implement toJSON and other utility functions.
The other Map this class has is _propertyChangeMap: Map<string, EventEmitter<{newvalue,oldvalue}>;
We also have a function called... $watch, which takes a string and a callback function.
This class can be extended by Entities as well as components or services
I'm happy to share, the caveat is your properties must look like this:
public get foo(): string {
return this._get("foo");
}
public set foo(value:string) {
this._set("foo", value);
}
--------------------------------------------------------------------
import { EventEmitter } from '@angular/core';
export abstract class MappedPropertyClass {
private _properties: Map<string, any>;
private _propertyChangeMap: Map<string, EventEmitter<{ newvalue, oldvalue }>>;
protected _set(propertyName: string, propertyValue: any) {
let oldValue = this._get(propertyName);
this._properties.set(propertyName, propertyValue);
this.getPropertyChangeEmitter(propertyName).emit({ newvalue:
propertyValue, oldvalue: oldValue });
}
protected _get(propertyName: string): any {
if (!this._properties.has(propertyName)) {
this._properties.set(propertyName, undefined);
}
return this._properties.get(propertyName);
}
protected get properties(): Map<string, any> {
var props = new Map<string, any>();
for (let key of this._properties.keys()) {
props.set(key, this._properties.get(key));
}
return props;
}
protected constructor() {
this._properties = new Map<string, any>();
this._propertyChangeMap = new Map<string, EventEmitter<{ newvalue: any,
oldvalue: any }>>();
}
private getPropertyChangeEmitter(propertyName: string): EventEmitter<{
newvalue, oldvalue }> {
if (!this._propertyChangeMap.has(propertyName)) {
this._propertyChangeMap.set(propertyName, new EventEmitter<{ newvalue,
oldvalue }>());
}
return this._propertyChangeMap.get(propertyName);
}
public $watch(propertyName: string, callback: (newvalue, oldvalue) => void):
any {
return this.getPropertyChangeEmitter(propertyName).subscribe((results) =>
{
callback(results.newvalue, results.oldvalue);
});
}
}
You could use custom setters to trigger your callback:
class Customer {
private _firstName: string
get firstName(): string {
return this._firstName
}
set firstName(firstName: string) {
this.valueChanged(this._firstName, firstName)
this._firstName = firstName
}
private _lastName: string
get lastName(): string {
return this._lastName
}
set lastName(lastName: string) {
this.valueChanged(this._lastName, lastName)
this._lastName = lastName
}
valueChanged: (oldVal, newVal) => void
constructor (valueChanged?: (oldVal, newVal) => void) {
// return an empty function if no callback was provided in case you don't need
// one or want to assign it later
this.valueChanged = valueChanged || (() => {})
}
}
Then just assign the callback when you create the object:
this.customer = new Customer((oldVal, newVal) => console.log(oldVal, newVal))
// or
this.customer = new Customer()
this.customer.valueChanged = (oldVal, newVal) => console.log(oldVal, newVal)