Is Angular2 detecting changes based on value equivalence or reference equality?

I am currently working with Angular2-RC.1 and I've noticed a significant decrease in performance when setting up a component with a large dataset. The component I'm using is a tabular one (which involves Handsontable) and it has an Input property called "data" that can be bound to external data. Typically, this property is connected to a sizable array containing around one hundred thousand rows.

Upon setting the large dataset, I've observed that the change detection process triggers a full comparison of values across the entire array within the host component (not the original owner of the input property).

@Component({
    selector: "ha-spreadsheet",
    template: "<hot-table [data]="data"></hot-table>",
    directives: [ HotTable ],
    encapsulation: ViewEncapsulation.Emulated
})
export class Spreadsheet implements OnActivate {
    data: { rows: Array<Array<number>> };
    load(service) { this.data = service.getLargeDataSet(); }
}

Here, I present a callstack indicating that the change detection operation is initiated throughout the entirety of the data. (the bold method represents the automatically generated runtime change detection function for my host component) rather than simply comparing references.

https://i.sstatic.net/Kvagp.png

Is this behavior intentional?

Answer №1

After conducting thorough research, I was able to arrive at the solution independently. The unique aspect of the standalone change detection process lies in its comparison of references, a behavior deliberately incorporated into its design.

However, it is crucial to note that when Production mode is disabled, extra validations are implemented to perform equivalence testing on the data of your component.

Answer №2

Even though @Jairo has already provided an answer to the question, I feel it's important to elaborate on the code flow he mentioned in a comment on his response (so I don't have to search through the source code again for this information):

When change detection occurs, the following code snippet from view_utils.ts is executed:

export function checkBinding(throwOnChange: boolean, oldValue: any, newValue: any): boolean {
  if (throwOnChange) {  // <<-------  this is set to true in devMode
    if (!devModeEqual(oldValue, newValue)) {
      throw new ExpressionChangedAfterItHasBeenCheckedException(oldValue, newValue, null);
    }
    return false;
  } else {
    return !looseIdentical(oldValue, newValue);  // <<--- so this runs in prodMode
  }
}

Here is the method from change_detection_util.ts that only operates in devMode:

export function devModeEqual(a: any, b: any): boolean {
  if (isListLikeIterable(a) && isListLikeIterable(b)) {
    return areIterablesEqual(a, b, devModeEqual);  // <<--- iterates over all items in a and b!
  } else if (!isListLikeIterable(a) && !isPrimitive(a) && !isListLikeIterable(b) &&
             !isPrimitive(b)) {
    return true;
  } else {
    return looseIdentical(a, b);
  }
}

In devMode change detection, when a template binding involves an iterable object – such as

[arrayInputProperty]="parentArray"
Angular actually iterates through all items (e.g., parentArray) and compares them, unlike the simple looseIdentical() check performed in prodMode. This behavior can significantly impact performance with large iterables, as seen in the situation discussed earlier.

areIterablesEqual() resides in collection.ts, simply iterating through and comparing each item in the iterables. (I omitted the code since it's straightforward.)

The looseIdentical() function from lang.ts represents what many of us believed change detection always did -- be it in devMode or prodMode:

export function looseIdentical(a, b): boolean {
  return a === b || typeof a === "number" && typeof b === "number" && isNaN(a) && isNaN(b);
}

Thank you @Jairo for delving into this topic.

Note to self: to easily locate the auto-generated change detection object Angular creates for a component, insert {{aMethod()}} in the template and add a breakpoint inside the aMethod(). Once the breakpoint triggers, the View*.detectChangesInternal() methods should appear on the call stack.

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

Binding objects and properties from Angular2/Typescript to a template

Disclaimer: Seeking insight on the correct approach or any additional information _____________________________________________________________________ ANGULAR1 When working with angular1, we had the option to define our objects in the following ma ...

Issues with Loading childComponent in Angular 5 Parent Component

How can I successfully load an internal component, specifically the store-top component at app/stores/store/store-top.component? I want to display the store-top content in the store component when a user clicks on any of the stores from a list. Any sugges ...

The promise chain from the ngbModal.open function is being bypassed

I'm currently working on implementing data editing within a component. My task involves checking if any of the data fields have been altered, and if so, prompting a confirmation pop-up to appear. If the user confirms the change, the data will then be ...

Oops! The program encountered an issue on the production environment, but it's running smoothly

When I execute Webpack using the command node node_modules/webpack/bin/webpack. js --env. prod An error message is displayed below. However, when running in --env. dev mode, the command executes without any issues. Can't resolve './../$$_gen ...

Accessing props in setup function in Vue 3

I am encountering issues when trying to access the props value (an array) in my composition API setup. The component I have is called DropDown, and I am passing it an array of objects. Here's what I need to achieve: export default { emits: ['up ...

Error: Unable to access 'nativeElement' property from undefined object when trying to read HTML element in Angular with Jasmine testing

There is a failure in the below case, while the same scenario passes in another location. it('login labels', () => { const terms = fixture.nativeElement as HTMLElement; expect(terms.querySelector('#LoginUsernameLabel')?.tex ...

The patchState function is iterated within the NGRX component-store effect

My program is stuck in an infinite loop and I can't figure out why. interface LatestAppointmentsWidgetState { loading: boolean; page: number; pagedData: Paged<Appointment>; } @Injectable() export class LatestAppointmentsWidg ...

Express not functioning properly with custom error handler

I'm facing an issue while trying to implement a custom error handler for my Express routes. Despite following the instructions in the documentation which recommend placing the custom handler at the end of the use chain, it seems that the default error ...

Upgrade from using fetch to utilize await in order to achieve the same outcome

After transitioning a one-time fetch request code snippet to my API, I encountered the following: let response = await fetch(visitURL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization& ...

Testing the addition of a dynamic class to an HTML button using Jasmine unit tests

I am brand new to Jasmine and currently in the process of grasping how to write Unit tests for my components in Angular 4. One issue I encountered is when I attempt to add a class to the button's classList within the ngOnInit() lifecycle hook of the C ...

Implementing strong security measures for Angular and .Net Core APIs using Azure AD authentication and defining specific application roles

My system is a combination of Angular front end and .Net Core API back end. Both are set up as distinct Active Directory apps in the Azure Portal. As a result, both applications are protected by Azure AD. The API is exposed and interacted with by authenti ...

Troubleshooting connectivity problems: SocketIO integration with microservices on a Kubernetes platform

I have organized my system using microservices architecture, with separate services for client interactions, orders, payments, and more. Each of these services runs on its own express server. Now, I am looking to integrate real-time feedback functionality ...

Best practices for managing backend errors with Next.js 14

Currently, I am developing a project in Next.js 14 and I have set up my API requests using fetch within a handler.tsx file as shown below: async function getPositions() { const response = await fetch( process.env.BASE_API_URL + "/positions?enabl ...

Exploring Angular 2's nested navigation using the latest router technology

Is there a way to implement nested navigation in Angular? I had this functionality with the previous router setup. { path: '/admin/...', component: AdminLayoutComponent } It seems that since rc1 of angular2, this feature is no longer supported. ...

What is causing this TypeScript error to be raised by this statement?

Can you explain why the following statement is throwing a type error? const x: Chat = { ...doc.data(), id: doc.id } The error message states: Type '{ id: string; }' is missing the following properties from type 'Chat': message, name, ...

What is the most efficient way to convert a JSON object into a URL search parameter using as few characters as possible?

Challenge: On my web app, users can adjust settings to create or edit generative artworks and then share their creations via a shortened link. The objective is to store all the data needed to replicate the artwork in the URL within 280 characters. Initia ...

The attribute "property" is not found in the specified type of "Request<ParamsDictionary>"

Struggling to enhance the Request interface in the express package with custom properties, I keep encountering this TypeScript error: TS2339: Property '' does not exist on type 'Request<ParamsDictionary>'. Any ideas on how to re ...

The vertical scrolling functionality of the MUI DataGrid in Safari may occasionally fail to work

Utilizing the <Box> component from MUI core and the <DataGrid> component from MUIX, along with some other components, I have created a data grid that has the following appearance: https://i.sstatic.net/Gc8sP.png When the number of rows exceed ...

Having trouble installing angular/cli on Windows7 64 bit using npm?

I am currently facing an issue while attempting to install angular-cli using the latest versions of npm (5.3.0) and node (v8.2.1) on a Windows7 64-bit environment. Both npm and node are functioning properly as expected. However, the installation process f ...

"Everything seems to be in order with `ng serve`, but `ionic serve`

My current project involves using Ionic version 6.19.0, but it has recently run into some unexpected compiling issues. Every time I attempt to execute 'ionic serve,' I encounter an Error: spawn UNKNOWN. Here is the content of my package.json fil ...