Detecting parent changes in Angular 2 child components

Currently, I am facing an issue with a component that displays a list of 'items' which are components created using a selector. I have a checkbox that I want to update the 'state' of all child components when clicked.

Struggling to find the right solution for this problem. For more information, please refer to Plunkr.

//our root app component
import {Component, EventEmitter} from 'angular2/core'

class Item {
  name: boolean;

  constructor(name: string) {
    this.name = name;
  }
}

@Component({
  selector: 'my-item',
  template: `
    <div>
      <label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
    </div>
  `
})
export class MyItemComponent {
  state: boolean = false;
}

@Component({
  selector: 'my-app',
  template: `
    <div style="border: 1px solid red;">
      <label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
    </div>
    <div *ngFor="#item of items">
      <my-item></my-item>
    </div>
  `,
  directives: [MyItemComponent]
})
export class App {
  state: boolean = true;
  items: Item[] = [];

  constructor() {
    this.items.push(new Item("hello"));
    this.items.push(new Item("test"));
  }
}

Answer №1

Latest Update

@Component({
  selector: 'my-item',
  inputs: ['status']; // new attribute
  template: `
    <div>
      <label><input type="checkbox" [(ngModel)]="status"/> {{status}}</label>
    </div>
  `
})
export class MyItemComponent {
  status: boolean = false;
}

Usage example:

<my-item [status]="status"></my-item>

Original Code

In Angular, changes in arrays may not be detected by the change detection system.
To resolve this issue, try the following:

constructor() {
    this.items.push(new Item("hello"));
    this.items.push(new Item("test"));
    this.items = this.items.slice();
  }

By assigning a new array (a copy) to this.items, Angular will detect the change.

In the MyItem component, ensure there is an input attribute

@Component({
  selector: 'my-item',
  inputs: ['items']; // new attribute
  template: `
    <div>
      <label><input type="checkbox" [(ngModel)]="status"/> {{status}}</label>
    </div>
  `
})
export class MyItemComponent {
  status: boolean = false;
  items: Item[]; // addition
}

To establish the connection:

<my-item [items]="items"></my-item>

To trigger code execution in MyItemComponent on changes to items, implement ngOnChanges(). For more details, refer to Angular OnChanges Interface Documentation

export class MyItemComponent {
  status: boolean = false;
  items: Item[]; // addition
  ngOnChanges(changes: {[propName: string]: SimpleChange}) {
    console.log('ngOnChanges - myProp = ' + changes['items'].currentValue);
  }
}

Answer №2

Indeed, what @Günter mentioned is undeniably accurate!

However, upon reviewing your plunkr code, I noticed a couple of errors:

@Component({
  selector: 'my-item',
  template: `
    <div>Hello</div>
  `
}) // <------- Rectify by removing the semicolon
export class MyItemComponent {

}

In addition, ensure that you include the component in the directives property:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
    </div>
    <div *ngFor="#item of items">
      <my-item></my-item>
    </div>
  `,
  directives: [MyItemComponent] // <-------- Ensure to add the MyItemComponent component here
})
export class App {
  (...)
}

Update

You can utilize the @ViewChildren decorator to directly access child components.

@Component({
  selector: 'my-app',
  template: `
    (...)
  `,
  directives: [MyItemComponent]
})
export class App {
  (...)
  @ViewChildren(MyItemComponent)
  children:MyItemComponent[];
  (...)
}

Subsequently, implement a mechanism on your checkbox to detect changes and modify the state of child components accordingly:

@Component({
  selector: 'my-app',
  template: `
    <div style="border: 1px solid red;">
      <label><input type="checkbox" [(ngModel)]="state"  
            [ngFormControl]="stateCtrl"/> {{state}}</label>
    </div>
    <div *ngFor="#item of items">
      <my-item></my-item>
    </div>
  `,
  directives: [MyItemComponent]
})
export class App {
  (...)
  constructor() {
    this.items.push(new Item("hello"));
    this.items.push(new Item("test"));

    this.stateCtrl = new Control();
    this.stateCtrl.valueChanges.subscribe(
      data => {
        this.children._results.forEach(child => {
          child.state = data;
        });
      });
  }
}

I have made the necessary adjustments to your plunkr using this approach: https://plnkr.co/edit/nAA2VxZmWy0d4lljvPpU?p=preview.

Refer to this link for further information: Why is that i can't inject the child component values into parent component?

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

What is the best way to modify the underline style of a tab in Material UI?

I'm trying to customize the underline of: https://i.stack.imgur.com/J2R1z.png Currently, I am using material ui version 4.12.3 The code snippet for generating my tabs is below: function renderTabs(): JSX.Element { return ( <Tabs className={cla ...

Child component experiencing issues with Materialize Pagination and Sorting functionalities not functioning

New to materialize pagination and currently working on the hierarchy below: app modules > list > list.component app.component Implemented a sample code example in app.component which worked perfectly. However, encountered issues when trying to imp ...

The creation of a parameterized function that doubles as an object property

interface item { first: string; last: string; } const itemList = Item[]; updateAttribute = (index, attributeToUpdate) => { itemList[index].attributeToUpdate = "New first/last" } The snippet above showcases an interface named item with propertie ...

What is the syntax for creating multi-line function definitions in TypeScript?

When it comes to writing functions with numerous parameters, do you typically format them like this: function foo( a: number, b: string, c: boolean): boolean { .... } or do you prefer formatting them like this: function foo( a: number, ...

What could be causing the ion-item click to malfunction in Ionic 4 on iOS devices?

ion-item clickable event is not working on iOS but it works fine on Android. I have been searching for a solution and have wasted a lot of time on it. Can someone please help me with this issue? <ion-content> <div> <ion-sear ...

Ways to change the Date format to something like [25 Jan 2024]

https://i.sstatic.net/BvSc8.png I am looking to convert the date format to appear as "25 Jan 2024" in my Angular application. Currently, the date is displayed as "25-01-2024" but I want it to be in the format "25 Jan 2024". Can anyone help me resolve this ...

Error message: Unable to locate module (webpack)/hot/emitter when running ionic serve with Ionic 4

Current Technology Stack: Node v10.15.1 Ionic 4.10.1 Upon running ionic serve, an error is encountered: ERROR in (webpack)/hot/emitter.js [ng] Module not found: Error: Can't resolve 'events' in '/zazou/node_modules/@angular-de ...

Creating Versatile Functions for HttpClient Wrapping

Scenario: In my set of services, I find myself repeatedly writing code for data calls which results in a lot of duplicated code. To streamline the process and reduce redundancy, I am looking to implement a wrapper function: All these functions essentiall ...

Angular 9 Material table row expansion feature does not automatically hide

I attempted to experiment with my first Angular application using this example as a beginner and encountered partial success: the expandable row initially appears empty before clicking on it, after which it displays as expected. I am unsure why direct expa ...

Disable JavaScript import optimization for a specific file in IntelliJIDEA

I recently came across a tutorial on Angular 2 Google maps that I was following: The tutorial included the following import statement: import { } from 'googlemaps'; However, I encountered a problem where IntelliJ recognized this as an empty im ...

Tips for preloading a TypeScript class using the -r option of ts-node

Imagine you have a file called lib.ts that contains the following code: export class A {} console.log('script loaded'); Now, if you launch the ts-node REPL using the command: npx ts-node -r ./lib.ts, you will see that it outputs "script loaded," ...

Having trouble with importing files from a different folder in a React Typescript project

I have a specific folder arrangement set up https://i.sstatic.net/GFOYv.png My goal is to bring both MessageList.tsx and MessageSent.tsx into my Chat.tsx file // Chat.tsx import React from 'react' import {MessageList, MessageSent} from "./ ...

In Typescript, an index signature parameter can only be of type 'string' or 'number'

I'm facing an issue with a generic type that defaults to string: interface EntityState<typeOfID = string> { entities: { [ id: typeOfID]: any }; } The error I receive is: An index signature parameter type must be either 'string' or ...

Tips for obtaining the iframe #document with cheeriojs?

I've been struggling to scrape the anime videos page [jkanime], specifically with extracting the mp4 video formats embedded in an iframe #document. Despite trying to use cheerio for querying, I've only managed to retrieve src links from Facebook ...

Vue 3 Composable console error: Unable to access properties of undefined (specifically 'isError') due to TypeError

I recently developed a Vue 3 / TypeScript Composable for uploading images to Firebase storage. The code snippet below illustrates the structure of the ImageUpload interface: interface ImageUpload { uploadTask?: UploadTask; downloadURL?: string; progr ...

attempting to pass a boolean type through props resulting in a type error

Hey, check out this component I created: import * as Styled from './styles'; export type HeadingProps = { children: React.ReactNode | string; colorDark: boolean; }; export const Heading = ({ children, colorDark }: HeadingProps) => { re ...

Using Angular to Generate a File from Form Input and Delivering it to the User

I am attempting to develop a feature in Angular 9 that takes user input from a textarea, processes it, and then presents it back to the user as a downloadable (txt) file. The structure of the form in app.component.html is as follows: <form (ngSubmit)= ...

Using Angular to Access External Services with Self-Signed Certificates

Is there a way to interact with external self-signed HTTPS services in Angular by either passing or attaching the certificate used by the service? I'm not too familiar with Angular, but I know that it's possible in the cURL library to attach a si ...

Tips on formatting the date model field in an Angular 2 form

I have a model with a date field in MySQL format "yyyy-mm-dd hh:ii:ss" When displaying this field in my form, I want to show it in an input with a custom format: "dd/mm/yyyy" <input [(ngModel)]="model.date" name="date" view-format="DD/MM/YYYY" model-f ...

Incorporate OpenLayers 4 into your Angular 5 application

I'd like to integrate OpenLayers 4 into Angular 5 for a project. My main goal is to implement the QuickStart example provided on the official OpenLayers Site. This is what I have accomplished so far: npm install ol --save to download the OpenLayer ...