How to watch component binding change using Angular component
Asked Answered
R

6

41

How can I listen to angular component binding change and perform actions?

angular.module('myapp')
    .component('myComponent', {
        templateUrl: 'some.html',
        controller: MyController,
        controllerAs: 'myCtrl',
        bindings: {
            items: '<'
        }
    });

now when items changes I want to perform another action using this value,
How can I do it?

Recruitment answered 28/2, 2016 at 10:1 Comment(0)
G
30

now when items changes I want to perform another action using this value, How can I do it?

But I want to avoid using the dying $scope

If you don't want to use $scope you can use a property setter to detect any changes e.g. :

class MyController {
    private _items: string[] = []
    set items(value:string[]){
        this._items = value;
        console.log('Items changed:',value);
    }
    get items():string[]{
        return this._items;
    }
}

const ctrl = new MyController();
ctrl.items = ['hello','world']; // will also log to the console

Please note that you shouldn't use it for complex logic (reasons : https://basarat.gitbooks.io/typescript/content/docs/tips/propertySetters.html) 🌹

Grounder answered 8/3, 2016 at 5:29 Comment(0)
R
68

You can add the $onChanges method to the controller

$onChanges(changesObj) is called whenever one-way bindings are updated. The changesObj is a hash whose keys are the names of the bound properties that have changed, and the values are an object of the form.

Following example handles canChange change event.

angular.module('app.components', [])
.component('changeHandler', {
  controller: function ChangeHandlerController() {
    this.$onChanges = function (changes) {
      if (changes.canChange) 
       this.performActionWithValueOf(changes.canChange);
    };
  },
  bindings: {
    canChange: '<'
  },
  templateUrl: 'change-handler.html'
});

Requires AngularJS >= 1.5.3 and works only with one-way data-binding (as in the example above).

Docs: https://docs.angularjs.org/guide/component

Reference: http://blog.thoughtram.io/angularjs/2016/03/29/exploring-angular-1.5-lifecycle-hooks.html

Radiative answered 5/5, 2016 at 11:16 Comment(1)
In case it helps anyone else: note that this only works with the newer one-way binding syntax ("<"), and not with the more "traditional" 2-way binding using "=". It's documented, but easy to miss, as I learned the hard way. :-p – Interlingua
G
30

now when items changes I want to perform another action using this value, How can I do it?

But I want to avoid using the dying $scope

If you don't want to use $scope you can use a property setter to detect any changes e.g. :

class MyController {
    private _items: string[] = []
    set items(value:string[]){
        this._items = value;
        console.log('Items changed:',value);
    }
    get items():string[]{
        return this._items;
    }
}

const ctrl = new MyController();
ctrl.items = ['hello','world']; // will also log to the console

Please note that you shouldn't use it for complex logic (reasons : https://basarat.gitbooks.io/typescript/content/docs/tips/propertySetters.html) 🌹

Grounder answered 8/3, 2016 at 5:29 Comment(0)
R
7

Here's an ES5.1 version of basarat's answer:

function MyController() {
  var items = [];

  Object.defineProperty(this, 'items', {
    get: function() {
      return items;
    },

    set: function(newVal) {
      items = newVal;
      console.log('Items changed:', newVal);
    }
  });
}

Using Object.defineProperty(). Supported by all major browsers and IE9+.

Riproaring answered 27/4, 2016 at 13:1 Comment(0)
J
3

I've discovered a way but not sure it's the most efficient. First bring in $scope as a dependency and set it to this._scope or the like in your constructor. I have the following then in my $onInit function:

this._scope.$watch(() => {
    return this.items;
  },
  (newVal, oldVal) => {
    // Do what you have to here
  });

It's highly inspired by the answer here: Angularjs: 'controller as syntax' and $watch

Hope it helps, it's what I'm going to use until I'm told otherwise.

Jed answered 29/2, 2016 at 21:36 Comment(2)
Thanks for your answer! But I want to avoid using the dying $scope – Recruitment
Yeah, it felt dirty since the "this" variable should really be the scope and have the watching setup. I've now had to do this in almost all of my components! The toggling of the value from null to something has worked fine, but just changing the contents of the watched items still remains a mystery. – Jed
D
0

Currently you can't use angular watchers without $scope as change detection is based around $scope. Even if you use expressions in HTML it would delegate watch functionality to $scope.

Even if you create some other mechanism to watch you will need to remember to unwatch manually - and with $scope it's done automatically.

Downcomer answered 7/3, 2016 at 21:23 Comment(0)
P
-1

This approach might help:

import { Input } from '@angular/core';

class MyComponent {
  @Input set items(value) {
    if (this._items !== value) {
      console.log(`The value has been changed from "${this._items}" to "${value}"`);
      this._items = value;
    }
  }

  private _items;  
  
  get items() {
    return this._items;
  }
}
Pennyworth answered 14/6, 2021 at 18:46 Comment(1)
The question is for AngularJS, your answer is for Angular. – With

© 2022 - 2024 — McMap. All rights reserved.