title: Prevent ngOnChanges from firing after emitting event (Angular 2+)
body: <p>In Angular 2+, custom two-way databinding can be accomplished by using <code>@Input</code> and <code>@Output</code> parameters. So if I want a child component to communicate with a third party plugin, I could do it as follows:</p>
  
  <pre><code>export class TestComponent implements OnInit, OnChanges {
      @Input() value: number;
      @Output() valueChange = new EventEmitter&lt;number&gt;();
  
      ngOnInit() {
          // Create an event handler which updates the parent component with the new value
          // from the third party plugin.
  
          thirdPartyPlugin.onSomeEvent(newValue =&gt; {
              this.valueChange.emit(newValue);
          });
      }
  
      ngOnChanges() {
          // Update the third party plugin with the new value from the parent component
  
          thirdPartyPlugin.setValue(this.value);
      }
  }
  </code></pre>
  
  <p>And use it like this:</p>
  
  <pre><code>&lt;test-component [(value)]="value"&gt;&lt;/test-component&gt;
  </code></pre>
  
  <p>After the third party plugin fires an event to notify us of a change, the child component updates the parent component by calling <code>this.valueChange.emit(newValue)</code>. The issue is that <code>ngOnChanges</code> then fires in the child component because the parent component's value has changed, which causes <code>thirdPartyPlugin.setValue(this.value)</code> to be called. But the plugin is already in the correct state, so this is a potentially unnecessary/expensive re-render.</p>
  
  <p>So what I often do is create a flag property in my child component:</p>
  
  <pre><code>export class TestComponent implements OnInit, OnChanges {
      ignoreModelChange = false;
  
      ngOnInit() {
          // Create an event handler which updates the parent component with the new value
          // from the third party plugin.
  
          thirdPartyPlugin.onSomeEvent(newValue =&gt; {
              // Set ignoreModelChange to true if ngChanges will fire, so that we avoid an
              // unnecessary (and potentially expensive) re-render.
  
              if (this.value === newValue) {
                  return;
              }
  
              ignoreModelChange = true;
  
              this.valueChange.emit(newValue);
          });
      }
  
      ngOnChanges() {
          if (ignoreModelChange) {
              ignoreModelChange = false;
  
              return;
          }
  
          thirdPartyPlugin.setValue(this.value);
      }
  }
  </code></pre>
  
  <p>But this feels like a hack.</p>
  
  <p>In Angular 1, directives which took in a parameter using the <code>=</code> binding had the same exact issue. So instead, I would accomplish custom two-way databinding by requiring <code>ngModelController</code>, which did not cause a re-render after a model update:</p>
  
  <pre><code>// Update the parent model with the new value from the third party plugin. After the model
  // is updated, $render will not fire, so we don't have to worry about a re-render.
  
  thirdPartyPlugin.onSomeEvent(function (newValue) {
      scope.$apply(function () {
          ngModelCtrl.$setViewValue(newValue);
      });
  });
  
  // Update the third party plugin with the new value from the parent model. This will only
  // fire if the parent scope changed the model (not when we call $setViewValue).
  
  ngModelCtrl.$render = function () {
      thirdPartyPlugin.setValue(ngModelCtrl.$viewValue);
  };
  </code></pre>
  
  <p>This worked, but <code>ngModelController</code> really seems to be designed for form elements (it has built in validation, etc.). So it felt a bit odd to use it in custom directives which are not form elements.</p>
  
  <p><strong>Question: Is there a best practice in Angular 2+ for implementing custom two-way databinding in a child component, which does not trigger <code>ngOnChanges</code> in the child component after updating the parent component using <code>EventEmitter</code>? Or should I integrate with <code>ngModel</code> just as I did in Angular 1, even if my child component is not a form element?</strong></p>
  
  <p>Thanks in advance!</p>
  
  <hr>
  
  <p><strong>Update</strong>: I checked out <a href="https://hackernoon.com/everything-you-need-to-know-about-change-detection-in-angular-8006c51d206f" rel="noreferrer">Everything you need to know about change detection in Angular</a> suggested by @Maximus in the comments. It looks like the <code>detach</code> method on <code>ChangeDetectorRef</code> will prevent any bindings in the <em>template</em> from being updated, which could help with performance if that's your situation. But it does not prevent <code>ngOnChanges</code> from being called:</p>
  
  <pre><code>thirdPartyPlugin.onSomeEvent(newValue =&gt; {
      // ngOnChanges will still fire after calling emit
  
      this.changeDetectorRef.detach();
      this.valueChange.emit(newValue);
  });
  </code></pre>
  
  <p>So far I haven't found a way to accomplish this using Angular's change detection (but I learned a lot in the process!).</p>
  
  <p>I ended up trying this with <code>ngModel</code> and <code>ControlValueAccessor</code>. This seems to accomplish what I need since it behaves as <code>ngModelController</code> in Angular 1:</p>
  
  <pre><code>export class TestComponentUsingNgModel implements ControlValueAccessor, OnInit {
      value: number;
  
      // Angular will pass us this function to invoke when we change the model
  
      onChange = (fn: any) =&gt; { };
  
      ngOnInit() {
          thirdPartyPlugin.onSomeEvent(newValue =&gt; {
              this.value = newValue;
  
              // Tell Angular to update the parent component with the new value from the third
              // party plugin
  
              this.onChange(newValue);
          });
      }
  
      // Update the third party plugin with the new value from the parent component. This
      // will only fire if the parent component changed the model (not when we call
      // this.onChange).
  
      writeValue(newValue: number) {
          this.value = newValue;
  
          thirdPartyPlugin.setValue(this.value);
      }
  
      registerOnChange(fn: any) {
          this.onChange = fn;
      }
  }
  </code></pre>
  
  <p>And use it like this:</p>
  
  <pre><code>&lt;test-component-using-ng-model [(ngModel)]="value"&gt;&lt;/test-component-using-ng-model&gt;
  </code></pre>
  
  <p>But again, if the custom component is not a form element, using <code>ngModel</code> seems a bit odd.</p>