Search code examples
typescriptweb-componentaurelia

Aurelia CollectionObserver handler is not triggered when collection is changed


I have an Aurelia component that is just supposed to display a list of items. The component's model has an "items" property which is bindable:

@bindable items = [];

The component template displays the list using a simple repeat attribute:

 <div repeat.for="item of items">  
    ${item.id}   </div>

When I push a new item in my bound array from the page's viewmodel where the component is used, I can see the item being added as the list is refreshed and the new item appears. My problem is that I need to perform other actions when the 'items' array is modified so I tried to add a collectionObserver to the component like this:

import {BindingEngine, inject} from 'aurelia-framework';

@inject(BindingEngine) 
export class GridControl {   

  @bindable items = [];

  constructor(bindingEngine) {
    this.items = []; 
    let subscription = bindingEngine.collectionObserver(this.items)
                                    .subscribe(this.listChanged);   
  }

  listChanged(splices) {
    // do some stuff   
  } 
}

But my 'listChanged' handler never gets called. Any idea why?


Solution

  • In the constructor, bindingEngine.collectionObserver(this.items) is called.

    Later, when the component is data-bound, this.items is assigned a different array instance via the data-binding. This different array instance is not the one you passed to the binding engine for observation.

    Try doing something like this:

    import {BindingEngine, inject} from 'aurelia-framework';
    
    @inject(BindingEngine) 
    export class GridControl {   
      @bindable items;
    
      constructor(bindingEngine) {
        this.bindingEngine = bindingEngine;
      }
    
      listChanged(splices) {
        // do some stuff   
      }
    
      subscribeItems() {
        if (this.items) {
          this.subscription = this.bindingEngine.collectionObserver(this.items)
            .subscribe(splices => this.listChanged(splices));
        }
      }
    
      unsubscribeItems() {
        if (this.subscription) {
          this.subscription.dispose();
          this.subscription = null;
        }
      }
    
      itemsChanged() {
        this.unsubscribeItems();
        this.subscribeItems();
      }
    
      unbind() {
        this.unsubscribeItems();
      }
    }