Two Way Binding on Custom Attribute Not Working

I’m trying to implement this solution given by jdanyow for dirty checking. I’m using TypeScript so I’ve adapted as best I can. For some reason, the isDirty flag on my VM is not being updated. Here is what I have:


export class Agency {
    public isDirty : boolean = false;
    public agency : IAgency = null;



   <require from="../resources/attributes/dirty"></require>
    <form dirty.bind="isDirty">
         <div class="input-group">
               <label>Agency Name</label>
               <input type="text" value.bind="" />
    <div if.bind="isDirty">
         <button>Save Changes</button>
         <button>Undo Changes</button>


import {bindable, bindingMode} from 'aurelia-framework';

export class DirtyCustomAttribute{
   private view: any;
   private bindings: any[] = [];
   @bindable({defaultBindingMode: bindingMode.twoWay }) value: any;

   static inject = [Element]
   constructor(public element : HTMLElement) {}

   created(view) {
      this.view = view;
   bind() {  
    this.bindings = this.view.bindings
        .filter(b => b.mode === bindingMode.twoWay && this.element.contains(;

    let i = this.bindings.length;
    let self = this;
    while (i--) {
        let binding = this.bindings[i];
        binding.dirtyTrackedUpdateSource = binding.updateSource;
        binding.updateSource = function (newValue) {
            if (!self.value) {
                self.value = true;

   unbind() { // See code in referenced link }

So, using logging, I can see that the updateSource() code is being called and the self.value flag is being set. However, the div with the buttons is never displayed. It would seem that the dirty.bind="isDirty" is not two-way binding like it should.

I’m sure I’m doing something wrong, but I can’t for the life of me see what it is.


I also posted this on StackOverflow. Someone there confirmed that the code, as shown, is working – he created a codesandbox that proves it. So, the one thing that I left out in trying to simplify my code was that the agency vm is being composed by another VM. I don’t understand why that would make a difference, but that is the only noticeable difference between my actual code and what I’ve listed here.

You can view my SO post to see the updated code with the compose as well as the codesandbox.

1 Like