How come ngOnChange is unable to detect changes in @Input elements when ngOnDetect is able to do so?

Check out this plunker

Please note: In order to see the effect, you need to restart the app after entering the link.

import {Component, OnInit, Input, OnChanges, DoCheck} from 'angular2/core'

@Component({
  selector: 'sub',
  template: `
    <li  *ngFor="#elem of sub_list">
      <div>{{elem['name']}}</div>
    </li>
    `
})
export class Sub {

  @Input()
  sub_list: Object[];

  ngOnInit(){
    console.log('init');
    console.log(this.sub_list);
  } 

  ngOnChanges(){
    console.log('change');
    console.log(this.sub_list);
  }

  ngDoCheck() {
    console.log('check');
    console.log(this.sub_list);
  }

}
@Component({
  selector: 'my-app',
  template: `
    <div>
      <sub
        [sub_list]="my_list"
      >
      </sub>

    </div>

    `,
  directives: [Sub]
})
export class App {

  my_list: Object[] = [];

  ngOnInit() {
      var vm = this;

    setTimeout(function() {
          for(var i = 0; i < 100; i++) {

        vm.my_list.push({
          'index' : i,
          'name'  : i
        });
      }
    }, 100);

  }
}

When trying to display this.sub_list in the ngOnChange method of Sub, the browser shows an empty list.

However, the ngDoCheck method accurately captures the change.

What could be the reason for this behavior?

Answer №1

If you encounter a situation where ngOnChanges is not being called for updates on an array, it is likely because Angular2 detects updates based on references. This means that if the reference of the entire array remains the same (such as when adding an element using the push method), the ngOnChanges method will not be triggered.

In your specific case, the array is null when ngOnChanges is called because it is invoked before the input element is set.

There are two ways to address this issue:

  • Update the entire array reference using methods like slice (after the push) or concat.

    this.myArray.push({...});
    this.myArray = this.myArray.slice();
    
  • Use the ngDoCheck method in conjunction with the IterableDiffers class to manually check for updates. This class allows you to register callbacks to be notified when an element is added or removed from an array.

For more information, you can refer to the following links:

Answer №2

Ah, now I understand. The ngOnChanges method is activated when you initially set a class field as an empty array, then later update the array within a timeout callback. As Thierry pointed out, these changes are not detected immediately.

Angular comes with the zone.js library, which can monitor all asynchronous events in your application. Zone recognizes that the timeout callback has been executed and initiates the ngDoCheck cycle, resulting in the correct log output.

For more information, check out this plunkr example.

Answer №3

When using ngOnChanges, it is important to note that changes to nested objects may not be detected if the reference remains the same. To solve this issue, it is recommended to create a new object reference each time:

const newObj = Object.assign({}, prevObj)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can I generate a checkbox in a database with a field that allows for enabling and disabling?

I am looking to design a table that includes questions, checkboxes, and text boxes. All the questions are stored in a database and called through an array. In addition, I want to create disabled textboxes that become enabled when their corresponding checkb ...

Modify the hue of the div as soon as a button on a separate webpage is

Looking for assistance with a page called "diagnosticoST" that contains four buttons (btn-institucional, btn-economico, btn-social, btn-natural). These buttons have different background colors until the survey inside them is completed. Once the user comple ...

What is the proper way to call document methods, like getElementByID, in a tsx file?

I am currently in the process of converting plain JavaScript files to TypeScript within a React application. However, I am facing an error with document when using methods like document.getElementById("login-username"). Can you guide me on how to referen ...

Error encountered when attempting to establish a connection between socket.io and express: network error

Similar Question: socket.io: Failed to load resource I'm having trouble getting a simple express + socket.io hello world app to work. I keep receiving the following error: "NetworkError: 404 Not Found - http:// localhost:3002/socket.io/socke ...

Tips on incorporating a dynamic variable value as a class name within a span tag

I am a beginner in the world of JavaScript. Below is the code snippet I am working with: result.push(`<span class="mark">${str.substring(h.startOffset, h.endOffset)}</span>`); Currently, I have a variable called var className = "dynamicvalue" ...

What are some ways to implement a pre-execution step, such as an interceptor, before Nextjs runs getStatic

When working with getStaticProps and getServerSideProps in Next.js, I need to intercept and add common header properties to all request calls that are executed server-side. axios.interceptors.request.use(function (config) { // Perform actions before ...

Even when I try to access the properties of the arguments object, it remains empty and has a zero

Why is the arguments object showing a length of zero when I pass parameters to the function, even though I can still access its properties? I am referring to the arguments object that is implemented by all JavaScript functions, allowing you to access the f ...

Showcasing a graphical representation with the help of Highcharts

I am currently exploring JavaScript and trying to familiarize myself with interactive charts using the HighCharts library. Unfortunately, I have been facing some challenges in getting the graph to print correctly. Despite attempting various examples, none ...

Appending the desired URL to the existing URL using an Ajax callback

Ever since I started working on a Drupal 7 project, I have been facing an issue with making an ajax call back. The problem arises when the URL I intend to use for the callback gets appended to the current page the user is viewing. It's quite puzzling ...

What is the reason for dirname not being a module attribute? (using the __ notation)

Currently, I am learning the fundamentals of Node.js. Based on the documentation, both __dirname and __filename are part of the module scope. As anticipated, when I use them like this: console.log(__dirname) console.log(__filename) They work correctly, d ...

Saving the Structure of an XML Document Using JQuery

Xml: <Data> <Cat> <Name>Fluffy</Name> </Cat> <Cat> <Name>Willy</Name> </Cat> </Data> JQuery: // ...Executing ajax requests... $(xml).find('Cat').each(function ...

Typescript Syntax for Inferring Types based on kind

I'm struggling to write proper TypeScript syntax for strict type inference in the following scenarios: Ensuring that the compiler correctly reports any missing switch/case options Confirming that the returned value matches the input kind by type typ ...

Ways to obtain every image placed onto an element

Using the img tag within div.image-block sets a background. Images can be added to .block3 through drag and drop. Is there a way to create a container that includes all elements from .image-block? <style> .image-block { position: relat ...

Ways to adjust the height of one panel based on the height of surrounding panels

I have a layout with three panels: two on the left side and one on the right. <div class="row"> <div class="col s6"> <div class="panel"> content1 </div> <div class="panel"> ...

When the page is first loaded, the select options dropdown using NgFor and NgValue does not display the initial object from the list

I am facing an issue with a select options dropdown that contains a list of objects. I have used ngValue to set the value of the dropdown as an object. However, upon page load, the dropdown does not display the first object from the list; it only shows obj ...

What causes TS2322 to only appear in specific situations for me?

I have been trying to create HTML documentation for my TypeScript project using Typedoc. Within one of the many files, there is a snippet of code: public doSomething(val: number | undefined | null | string): string | undefined | null { if (val === null ...

Unable to utilize ngForm when values are already predefined

I have an Angular application with Ionic 4. Here is the HTML code for my form: <form #formAuth="ngForm" (ngSubmit)="sendCode(formAuth)" method="post"> <ion-select placeholder="Country" ngModel name="area_code" interface="modal"> <io ...

React Native is facing difficulty in fetching pagination data which is causing rendering errors

Currently, I am working on fetching pagination data from an API. The process involves retrieving data from https://myapi/?page=1, then from https://myapi/?page=2, and so on. However, despite following this logic, I encountered an error that has left me puz ...

Navigating through an array's contents with RxJs

Is there a more efficient way to iterate over an array fetched from an observable using RxJS operators in order to generate and emit new individual ListingItem objects? onGetItemData(){ this.dataService.getItemData().subscribe((itemData) => { this.it ...

Utilizing a JavaScript variable within a jQuery function as an attribute

var image = "path.png"; Is it possible to include the 'image' variable in the jQuery function like this? $('#mapfoto').prepend('<img id="theImg" src="http://path.gr/" + image />'); ...