47

I have a directive and on it is an @Input that accepts a class.

@Directive({selector: 'my-directive'})
@View({directives: [CORE_DIRECTIVES]})
export class MyDirective  {
    @Input() inputSettings : SettingsClass;
    @Input() count : number;

   onChanges(map) {
      console.log('onChanges');
    }
}

The directive is used in html:

  ...
  <my-directive [input-settings]="settings" [count]="settings.count"></my-directive>
  ...

If the settings.count is changed then the onChanges will fire. If any other property on the settings class changes, then it will not fire.

How can I detect if there is a change to any property on settings?

Peter
  • 3,563
  • 5
  • 30
  • 43
  • My guess is this (currently?) doesn't work... maybe because `inputSettings` contains a reference to an instance of an object and since the reference itself doesn't change, Angular probably doesn't try to compare object properties so it does not see or report a change. (I hope I'm wrong, or I hope they fix this.) – Mark Rajcok Dec 07 '15 at 21:21
  • Indeed, [Victor Savkin's blog post](http://victorsavkin.com/post/133936129316/angular-immutability-and-encapsulation) confirmed my assumptions: "If the address object is mutable, some other component can update the street property without creating a new address object. If this happens, the onChanges hook won’t be called". – Mark Rajcok Dec 15 '15 at 19:53
  • you can also use getter and setter for the attribute input property – Ayyash Jan 18 '17 at 10:22
  • can you please explain your code sample? why would `onChanges()` ever be called? – phil294 Feb 23 '17 at 23:18

2 Answers2

60

Angular will only notice if the object has been changed to a different object (i.e., the object reference changed), so ngOnChanges() can't be used to solve your problem. See Victor Savkin's blog post for more information.

You could implement the ngDoCheck() method in your MyDirective class. That lifecycle hook is called "every time that the input properties of a component or a directive are checked. Use it to extend change detection by performing a custom check."

To implement your custom check method, you would first need to implement an .equals() method on class SettingsClass such that you could then write code something like the following in ngDoCheck():

ngDoCheck() {
   if(!this.inputSettings.equals(this.previousInputSettings)) {
      // inputSettings changed
      // some logic here to react to the change
      this.previousInputSettings = this.inputSettings;
   }
}
sfs
  • 431
  • 8
  • 16
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
1

An other solution , which is useful when you don't have control on the object you are passing to the component, is to use ViewChild and directly call a method to update the object :

In the child component add a function like :

public updateSettings(obj: SettingsClass) {
    this.inputSettings  = obj;
}

And in the parent component call the updateSettings function :

@Component({
    selector: 'app-mycomponnent',
    templateUrl: './my.component.html',
    styleUrls: ['./my.component.css']
})
export class PlayComponent implements OnInit {

    @ViewChild(MyDirectiveComponent,{static: false}) mydirective;

    elsewhereinthecode() {
        // When you need to update call : 
        mydirective.updateSettings(settingsObject);
    }
}
grunk
  • 14,718
  • 15
  • 67
  • 108