Monitoring modifications in elements within an array using Angular2

Currently using Angular 2 and typescript, I have an array in which I am utilizing DoCheck and IterableDiffer to monitor any changes. While I receive notifications when the array itself is modified, I do not get notified if a property within one of the objects in the array changes. I attempted using KeyValueDiffer without success. It seems like I might need to adjust how I am using "_differs.find". Any suggestions?

@Component({
    selector: 'test'
})
@View({
    template: `
    <div>hello!</div>`
})
export class MyComponent implements DoCheck {

private _differ: IterableDiffer;
private _differ2: KeyValueDiffer;
_myItems: MyItem[];
@Input()
set myItems(value: MyItem[]) {
    this._myItems = value;

    if (!this._differ && value) {
        this._differ = this._iterableDiffers.find([]).create(null);
    }
    if (!this._differ2 && value) {
        this._differ2 = this._differs.find(this._myItems).create(null);
    }
}
get myItems() {
    return this._myItems;
}

constructor(private _iterableDiffers: IterableDiffers, private _differs: KeyValueDiffers, private _myService: MyService) {
    this.myItems = _myService.getItems();
}

ngDoCheck() {

    var changes = this._differ.diff(this._myItems);

    if (changes) {
        changes.forEachAddedItem((record) => {
            console.log('added ' + record.item);
        });
        changes.forEachRemovedItem((record) => {
            console.log('removed ' + record.item);
        });
    }

    var changes2 = this._differ2.diff(this._myItems);
    if (changes2) {
        changes2.forEachChangedItem(
            (record) => {
                    console.log(record.key + "," + record.currentValue);
                }
            });
    }
  }
}

It appears challenging to receive notifications for changes within a property of MyItem.

Answer №1

To effectively compare objects in a list, it's essential to assess the variances within each object, rather than just looking at the list as a whole. The KeyValueDiffer function should be used on individual objects, not on arrays.

One approach is to create an object that holds all the KeyValueDiffer instances for the elements in your array:

constructor(private differs:  KeyValueDiffers) {
}

ngOnInit() {
  this.objDiffer = {};
  this.list.forEach((elt) => {
    this.objDiffer[elt] = this.differs.find(elt).create(null);
  });
}

Then, during the ngDoCheck lifecycle hook, iterate through the differs to detect any changes within each item of the array:

ngDoCheck() {
  this.list.forEach(elt => {
    var objDiffer = this.objDiffer[elt];
    var objChanges = objDiffer.diff(elt);
    if (objChanges) {
      objChanges.forEachChangedItem((elt) => {
        if (elt.key === 'prop1') {
          this.doSomethingIfProp1Change();
        }
      });
    }
  });
}

You can view a working example on this plunkr: http://plnkr.co/edit/JV7xcMhAuupnSdwrd8XB?p=preview

It's worth noting that I didn't address the change detection for the entire array, but you can implement both strategies concurrently. Additionally, ensure to update the list of KeyValueDiffers when items are added or removed from the array.

Answer №2

Inspired by Thierry Templiers response, I took his idea and tweaked it to suit my needs. Here is the solution I came up with:

Firstly, I had a private variable like this:

 private objDiffers: Array<KeyValueDiffer<string, any>>;

The goal was to monitor each element in an Array, specifically of type Array. So, my constructor ended up looking like this:

  constructor(
    private differs: KeyValueDiffers) {
    this.itemGroups = new Array<ItemGroup>();
    this.differ = this.differs.find(this.itemGroups).create();
  }

For ngOnInit method:

  public ngOnInit(): void {
    this.objDiffers = new Array<KeyValueDiffer<string, any>>();
    this.itemGroups.forEach((itemGroup, index) => {
      this.objDiffers[index] = this.differs.find(itemGroup).create();
    });
  }

and for ngDoCheck method:

ngDoCheck(): void {
    this.itemGroups.forEach((itemGroup, index) => {
      const objDiffer = this.objDiffers[index];
      const objChanges = objDiffer.diff(itemGroup);
      if (objChanges) {
        objChanges.forEachChangedItem((changedItem) => {
          console.log(changedItem.key);
        });
      }
    });
  }

With this setup, every time an item within the array undergoes a change, you will see a console log displaying the specific item and its altered property.

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

The variable 'string' has been declared, but it is never utilized or accessed

Currently delving into Typescript and facing an early error. Despite following a tutorial, I'm encountering multiple errors that I have attempted to comment out. Would greatly appreciate it if someone could shed some light on why these errors are occu ...

Tips for incorporating a child's cleaning tasks into the parent component

I am working with a parent and a child component in my project. The parent component functions as a page, while the child component needs to perform some cleanup tasks related to the database. My expectation is that when I close the parent page/component, ...

Unraveling the Perfect Jest Stack Trace

Currently, I am in the process of debugging some tests that were written with jest using typescript and it's causing quite a headache. Whenever a test or tested class runs Postgres SQL and encounters an error in the query, the stack trace provided is ...

Encountered a Runtime Error: Uncaught promise rejection - couldn't locate removeView

I am facing an issue with calling two API calls on the same page. When I use only one API call, everything works fine. However, when I try to make two API calls on the same page, I encounter the following error at runtime: Error Uncaught (in promise): r ...

What is the best way to retrieve the value from a chosen radio button?

Here is the HTML code snippet: <ion-list radio-group [(ngModel)]="portion" (ionChange)="getPortionType()"> <ion-list-header> Select Portion </ion-list-header> <ion-item *ngFor="let item of porti ...

Tips for preventing the overwriting of a JSON object in a React application

I'm trying to compare two JSON objects and identify the differing values in the second JSON object based on a specific key. My goal is to store these mismatched values in a new JSON object. The current issue I'm facing is that when there are mult ...

Leveraging Angular's capability to import files directly from the assets

I recently installed a library via npm and made some modifications to one of the modules. python.js If I delete the node_modules folder and run npm install, I am concerned that I will lose my changes. Is there a way to preserve these modifications by mov ...

The issue of NETWORK ERROR cannot be fixed through the use of axios

I'm attempting to communicate with a backend server that is currently offline using axios const backendClient = axios.create({ baseURL : env }); The API call is made here: export const createExpensesRecord = async (createExpenseRecordCmd) => { ...

Best location to define numerous dialog components

Currently, I have 8 custom Modals that are called in various places within my app. These modals are currently located inside the app.component.html as shown below: <agc class="app-content" [rows]="'auto 1fr'" [height]=" ...

Angular2: Property binding authorization is not implemented in any directive within an embedded template

I created a directive in Angular 2, but it is not working and returning a template parse error. Directive code : import { Directive, Input } from '@angular/core'; import { TemplateRef, ViewContainerRef } from '@angular/core'; import { ...

Getter and setter methods in Angular Typescript are returning undefined values

I am facing a challenge in my Angular project where I need a property within a class to return specific fields in an object. Although I have implemented this successfully in .Net before, I am encountering an issue with getting an "Undefined" value returned ...

What distinguishes between the methods of detecting falsy and truthy values?

While working with JavaScript / Typescript, I often find myself needing to verify if a length exists or if a value is true or false. So, the main query arises: are there any differences in performance or behavior when checking like this... const data = [ ...

Using TypeScript to call a class method from within another function

Currently, I am working on an Angular 2 application and struggling to grasp the concept of TypeScript's this scope. Within my TypeScript class named SharedService, there is a function called handleError. If this function encounters a 401 status, I wa ...

When attempting to update to Angular Material 8 using ng update, I ended up with @angular/* version ~9.0.0-next-0. Can anyone explain why there is this

While attempting to upgrade my Angular 7 application to Angular 8 using the instructions provided here, I encountered an issue with the last step: ng update @angular/material After running this command, the Angular Material packages were successfully u ...

Learn how to retrieve data from a parent component in Angular 8 by leveraging the window.opener property in a child window

On my Angular application, I have 2 separate pages - page1 and page2. Page1 has a button that, when clicked, opens page2. Since they are different components, I need to use window.opener to access data from page1 in page2 by calling window.opener.page1Func ...

Tips for accessing the Components.inputs array in Angular

I've noticed that in Angular, you have the ability to define inputs within the @Component decorator. @Component({ selector: 'my-component', changeDetection: ChangeDetectionStrategy.OnPush, template: '<ng-content></ng-c ...

Having issues with Angular http.post not sending data when using subscribe

I'm currently facing an issue with sending data to my API using post.subscribe. Despite the fact that no errors are being thrown, the data is not being sent successfully. It's important to note that the API itself is functioning perfectly. Belo ...

The request to search for "aq" on localhost at port 8100 using Ionic 2 resulted in a 404 error, indicating that the

Trying to create a basic app that utilizes an http request, but facing challenges with cors in ionic 2. To begin with, modifications were made to the ionic.config.json { "name": "weatherapp", "app_id": "", "v2": true, "typescript": true, "prox ...

Preventing going back to a previous step or disabling a step within the CDK Stepper functionality

In my Angular application, there is a CdkStepper with 4 steps that functions as expected. Each step must be completed in order, but users can always go back to the previous step if needed. For more information on CdkStepper: https://material.angular.io/cd ...

Confirm your identity without using a Single Sign-On (SSO) system

My current situation involves a PHP-based website where users login using credentials stored in a database. Additionally, we have another SPA website with .NET CORE as the API layer. Unfortunately, we do not have the option of utilizing a central authent ...