top of page
Search
  • Writer's pictureZsolt

The <dirty> secret of Formly and Angular 18



Formly is a a useful tool for generating forms, especially when you might have constantly changing requirements. With Formly, all you need is a new JSON from the backend and the job is done, it is as simple as: this.fields = [this.formlyJsonschema.toFieldConfig(schema); . Naturally, it has some limitations but is versatile and capable. So much so that it supports arrays, such as an element that might have 0 or more values of the same type, generating the FormControls at runtime as the user pleases.

This has one small issue though. Let’s try listening to changes on one such repeat element and click that red button.

model: any = {
  tasks: ['task1', 'taks2'] // form has initial elements when pristine
};

constructor() {
	this.form.valueChanges.pipe(takeUntilDestroyed()).subscribe((formVal) => {
    console.log('form value change ', formVal);
    console.log(this.form.pristine);
  });
}

The issue is twofold here. Partially Formly does not fire markForCheck and patch or setValue in a correct order, which I’m sure will be fixed. On the other hand, Angular has long lacked the capability to listen to pristine , dirty , touched or status changes.

Up until Angular 18 the best we could do was a monkey patch:

// inside a custom ControlValueAccessor descendant
const self = this;
const origFunc = this.control.markAsPristine;
this.control.markAsPristine = function () {
  origFunc.apply(this, arguments);
  console.log('Marked as pristine!');
}

// or inside a component using Formly
const origFunc = this.form.markAsPristine;
this.control.markAsPristine = () => {
  origFunc.apply(this.form, arguments); // arrow functions don't los context of 'this'
  console.log('Marked as pristine!');
}

Note how the second uses arrow function, because it uses ‘this’. If you want to see some more Angular examples of losing ‘this’, check this article.


But then came the change in Angular 18. Now we have the API to listen to changes on form state, so problem solved:

this.form.events.pipe(takeUntilDestroyed()).subscribe((event) => {
  if (event instanceof PristineChangeEvent) {
    console.log('form pristine change ', event.pristine);
    console.log(this.form.pristine);
  }
});


If you liked this post please don't forget to clap on the original on Medium:

Thank you




1 view0 comments
SIGN UP AND STAY UPDATED!

Thanks for submitting!

© 2019 ZD Engineering. Proudly created with Wix.com

bottom of page