# Change handler for ALL form elements in component

**URL:** <https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836>\
**Category:** Help Requests\
**Created:** [July 6, 2022, 9:56pm UTC](https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836 "2022-07-06T21:56:20Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![roemhildtg](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/roemhildtg/32/1910_2.png) [@roemhildtg](https://discourse.aurelia.io/u/roemhildtg)\
**Post date:** [July 6, 2022, 9:56pm UTC](https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836/1 "2022-07-06T21:56:21Z")

</div>

I’m using aurelia v1 for an app. We’re trying to add a “global” change handler for a component where we have a form and about a dozen radio/select/text elements. What I want to do is add a `isModified` property to the component and to use this to prevent navigation via `canDeactivate`.

Is there a straightforward way to make this happen in a component? IE when any input element changes, set `isModified = true` ?

Edit:

A little more detail - my current solution I can wrap my head around is to use change.delegate handlers on every single form element.

```nohighlight
<input change.delegate="isModified = true">
...

```

This is just very repetitive though and thought there might be an easier way, something like a global “change” handler to capture bubbling input change events.

---

<div class="post-metadata">

**Author:** ![manks](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/manks/32/1995_2.png) [@manks](https://discourse.aurelia.io/u/manks)\
**Post date:** [July 7, 2022, 12:26am UTC](https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836/2 "2022-07-07T00:26:51Z")

</div>

I don’t have a specific answer, but here are a few concepts you may end up using in your solution.

First, you can do change detection on a specific form field:

`view.component.ts`:

```auto
@inject(CanDeactiveFormService)
export class SomeViewComponent {

  @bindable({ defaultBindingMode: bindingMode.twoWay })
  firstName: string;

  public constructor(private canService: CanDeactiveFormService) {}

  firstNameChanged(newValue, oldValue) {
      this.canService.isModified = true;
  }
}

```

Then your canDeactivate functionality can check the service.

Of course, if you have your form fields in an object, that won’t work since Aurelia v1 doesn’t track deep changes. Instead, you’ll need to use a custom deep watch on your object.

`multi-properties-binding-engine.js`

```auto
import { inject, ObserverLocator, TaskQueue } from 'aurelia-framework';

@inject(ObserverLocator, TaskQueue)
export class MultiPropertiesBindingEngine {

    constructor(private observerLocator: ObserverLocator,
        private taskQueue: TaskQueue) {

    }

    /**
     * @param {object} obj
     * @param {string[]} props
     */
    propertiesObserver(obj: {}, props: string[]) {

        return {
            subscribe: (callback: Function) => {
                let isCallbackQueued = false;
                const unmarkQueued = () => {
                    isCallbackQueued = false;
                };
                const handler = () => {
                    if (!isCallbackQueued) {
                        this.taskQueue.queueMicroTask(unmarkQueued);
                        this.taskQueue.queueMicroTask(callback);
                        isCallbackQueued = true;
                    }
                };
                const observers = props.map(prop => this.observerLocator.getObserver(obj, prop));
                observers.forEach(observer => {
                    observer.subscribe(handler);
                });
                return {
                    observers: observers,
                    dispose: () => {
                        observers.forEach(observer => {
                            observer.unsubscribe(handler);
                        });
                    }
                };
            }
        }

    }
}

```

```auto

import { MultiPropertiesBindingEngine } from 'multi-properties-binding-engine';

@inject(Router, CanDeactiveFormService, MultiPropertiesBindingEngine)
export class AdminUserEditView {

    formData: SomeModel;

    private subscriptions: Disposable[] = [];

    constructor(
        private router: Router,
        private canService: CanDeactiveFormService,
        private bindingEngine: MultiPropertiesBindingEngine) {

    }

    async activate(params: any, routeConfig: RouteConfig): Promise<any> {
        this.subscriptions.push(this.bindingEngine
            .propertiesObserver(this.formData, ['status', 'text', 'category', 'dateFrom', 'dateTo'])
            .subscribe(this.formDataChanged.bind(this)));
    }

    formDataChanged(newValue, oldValue) {
      this.canService.isModified = true;
    }

    detached() {
        if (this.subscriptions && this.subscriptions.length) {
            this.subscriptions.forEach((disposable) => disposable.dispose());
            while (this.subscriptions.length) {
                this.subscriptions.pop();
            }
        }
    }
}

```

I haven’t looked into any type of reactive forms with Aurelia, but you should be able to subscribe to a form-level change event. This is an interesting case, and I’d be interested in learning how you solve it. There must be an elegant solution instead of such a hack above.

---

<div class="post-metadata">

**Author:** ![avrahamcool](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/avrahamcool/32/368_2.png) [@avrahamcool](https://discourse.aurelia.io/u/avrahamcool)\
**Post date:** [July 7, 2022, 9:37pm UTC](https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836/3 "2022-07-07T21:37:56Z")

</div>

create an object to hold your bounded properties.  
and use [How To Observe Objects In Aurelia Using The Binding Engine](https://discourse.aurelia.io/t/how-to-observe-objects-in-aurelia-using-the-binding-engine/23)  
in order to obsever all properties of the object using the same change handler.

i briefly looked at @manks answer.  
and it seemed too complicated.  
but you should compare the solutions and check what works better for you.

---

<div class="post-metadata">

**Author:** ![roemhildtg](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/roemhildtg/32/1910_2.png) [@roemhildtg](https://discourse.aurelia.io/u/roemhildtg)\
**Post date:** [July 8, 2022, 12:49pm UTC](https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836/4 "2022-07-08T12:49:18Z")

</div>

Both of these solutions seem a little complicated I guess ( I don’t mean any offense by the way, I’m just trying to find a simple solution to what would seem to me to be a common situation).

Here’s another workaround I found:

```nohighlight
class FormComponent {
   // object we are editing
   @bindable object = {};
   objectClone;
    activate(){ 
        this.objectClone = {...this.object};
    }
    
    canDeactivate(){
        if(deepEqual(this.object, this.objectClone)){
            return true;
        }
        return confirm('Form was modified but not saved, are you sure you want to continue?');
    }
    
    save(){
        // do validation , save to api, etc....
       .then(() => {
            this.objectClone = {...object};
        });
    }

```

Does this make sense ?

---

<div class="post-metadata">

**Author:** ![manks](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/manks/32/1995_2.png) [@manks](https://discourse.aurelia.io/u/manks)\
**Post date:** [July 8, 2022, 4:10pm UTC](https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836/5 "2022-07-08T16:10:26Z")

</div>

Certainly seems like a valid way to do it.

---

<div class="post-metadata">

**Author:** ![simonfox](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/simonfox/32/79_2.png) [@simonfox](https://discourse.aurelia.io/u/simonfox)\
**Post date:** [July 11, 2022, 1:07am UTC](https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836/6 "2022-07-11T01:07:40Z")

</div>

You can add a change handler at the root of the form…  
[https://gist.dumber.app/?gist=1cf631925453d40cbcd7f43c122bd324](https://gist.dumber.app/?gist=1cf631925453d40cbcd7f43c122bd324)

---

<div class="post-metadata">

**Author:** ![roemhildtg](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/roemhildtg/32/1910_2.png) [@roemhildtg](https://discourse.aurelia.io/u/roemhildtg)\
**Post date:** [July 15, 2022, 6:09pm UTC](https://discourse.aurelia.io/t/change-handler-for-all-form-elements-in-component/4836/7 "2022-07-15T18:09:45Z")

</div>

Whoa - I didn’t know you could add event handlers to parent elements! That is good to know!

🤯
