Angular - Best practices for exchanging feedback between sibling components

The title may not be the most descriptive, but I struggled to find a better way to convey my issue.

I've encountered a problem multiple times while working with angular. To illustrate, let's consider this scenario:

Imagine having a main component called MomComponent, which contains several instances of KidComponent.

Each KidComponent can be resized by the user, and if one is resized, all others should adjust accordingly.

A KidComponent should also function independently without any sibling components.

Here is a snippet of my code:

// mom.component.ts
import { Component } from '@angular/core';

@Component({
    selector: 'app-mom',
    templateUrl: './mom.component.html',
})
export class MomComponent {
    width: number;
}
<!-- mom.component.html -->

<app-kid [(width)]="width"></app-kid>

<app-kid [(width)]="width"></app-kid>

<app-kid [(width)]="width"></app-kid>
// kid.component.ts
import { Component } from '@angular/core';

@Component({
    selector: 'app-kid',
    templateUrl: './kid.component.html',
})
export class KidComponent {
    @Input() set width(w: number) {
        this._width = w;
        this.draw();
    }
    @Output() widthChange = new EventEmitter<number>();

    private _width = 100;

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        this._width = newWidth;
        this.draw();
    }

    draw() {
        // Drawing logic
    }
}

The issue here is that the resized kid component gets drawn twice - once due to internal calls and again because the mom's width variable updates.

To avoid this redundancy, I could modify the kid component like so:

// kid.component.ts
export class KidComponent {
    // [...]

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        // Removed these two lines:
        //
        // this._width = newWidth;
        // this.draw();
    }
  
    // [...]
}

However, this modification would prevent me from using KidComponent individually without sharing width information with other kids.


The current solution I have in mind involves:

// kid.component.ts
export class KidComponent {
    // [...]

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        if (!this.widthChange.observers.length) {
            // If there are no listeners for the event, then redraw internally
            this._width = newWidth;
            this.draw();
        } else {
            // If there are listeners, assume they will handle redrawing
        }
    }

    // [...]
}

Although functional, I am not entirely satisfied with this approach as just having an observer on widthChage emitter doesn't guarantee proper redrawing of the KidComponent.

So, my question remains: Is there a more effective approach that I might be overlooking?

Thanks!

Answer №1

Here's a simple suggestion based on my comment:

// kid.component.ts
import { Component } from '@angular/core';

@Component({
    selector: 'app-kid',
    templateUrl: './kid.component.html',
})
export class KidComponent {
    @Input() set width(w: number) {
        if(this._width === w) return;
        this._width = w;
        this.draw();
    }

    @Output() widthChange = new EventEmitter<number>();

    private _width = 100;

    onUserResize(newWidth: number) {
        this.width = newWith;
        this.widthChange.emit(newWidth);
    }

    draw() {
        // Drawing code
    }
}

In addition to the above suggestion, Brandon Tylor proposed using services for communication between siblings. However, considering that you only need one property, it may introduce unnecessary complexity.

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

Basic HTML Audio Player Featuring Several Customizable Variables

I have a unique API that manages music playback. Instead of playing audio in the browser, it is done through a Discord bot. Achievement Goal https://i.stack.imgur.com/w3WUJ.png Parameters: current: indicates the current position of the track (e.g. 2:3 ...

Why does `window.location.reload()` only refresh the home page and not the other pages when using Angular?

After transitioning from the home page to the menu page, I expect the menu page to refresh automatically once. However, when implementing the code below, the home page is refreshed first before navigating to the menu page without an auto-refresh. 1)Initia ...

Tips for specifying a custom type as the return value of a function and denote it as a promise

I encountered this code snippet: import * as Promise from 'bluebird'; import {Response} from '../commonInterfaces/httpService'; getCall(url: string, accessToken: string, extraHeaders: object): Promise<Response> { let headers ...

Revolutionize your Angular applications with dynamic template loading techniques

I recently developed a new component called componentB, which shares a similar template with another component, componentA. Currently, when a specific url is accessed, the original componentA is loaded. However, I want to load a slightly modified template ...

What are the reasons behind the issues encountered when enabling "Optimization" feature in Angular that affect the proper rendering of PrimeNg elements?

Angular Version : 9.x Primeng Version : 9.x We've encountered an issue with PrimeNg elements not rendering correctly in our dev/prod environments, although they work fine in the local environment. After some investigation, we found that the culprit ...

Can TypeScript provide a method for verifying infinite levels of nested arrays within a type?

Check out this example The concept behind this is having a type that can either be a single object or an array of objects. type SingleOrArray<T> = T | T[]; The structure in question looks like this: const area: ItemArea = [ { name: 'test1& ...

Retrieving the ngModel value in Ionic without triggering any actions

After trying to send the value of <ion-text> to a TypeScript file when a button is clicked on the same HTML page, I encountered an issue where it didn't work as expected. <ion-text [(ngModel)]='xy' ngDefaultControl >'vari ...

What steps can be taken to safeguard data while navigating within the Angular framework?

I am facing an issue with storing an array of items in a service (referred to as cart service) and displaying it in the component (cart.component.ts). The components bgview.component.ts and single.component.ts are involved in selecting individual items, wi ...

Expanding the capability of a function by inheriting properties of either type any or unknown

Can you explain why the values of P1 and P2 are different in these type definitions? type P1 = (() => 22) extends {[k:string]:any} ? 1:2 //`P1 == 1` type P2 = (() => 22) extends {[k:string]:unknown} ? 1:2 //`P2 == 2` ...

"Here's how you can mark an option as selected in Angular, either from the component or the HTML file

When it comes to my form, I have a select menu that sends data to an SQL database and then fetches it back when it is called for editing. The value being edited should be displayed in the select menu option as selected. Here's a peek at my code: < ...

Verifying TypeScript Class Instances with Node Module Type Checking

My current task involves converting our Vanilla JS node modules to TypeScript. I have rewritten them as classes, added new functionality, created a legacy wrapper, and set up the corresponding Webpack configuration. However, I am facing an issue with singl ...

Tips for managing a group of checkboxes in Angular 2 RC5

My task involves creating a form where users can edit their magazine subscriptions. Here is the code snippet I am working with: Component: export class OrderFormComponent { subscriptions = [ {id: 'weekly', display: 'Weekly new ...

What is the best approach for initializing and adding dataset in a database using Nest.JS when launching the application for the first time?

In managing my database, I have multiple tables that require default information such as categories, permissions, roles, and tags. It is crucial for me to ensure that this exact information has consistent IDs whenever the application is freshly launched on ...

Discover the wonders of utilizing @blur events on your custom Vue components!

Trying to create a customized component that mimics an input field with validation, I'm encountering issues with getting @Change, @blur, and other events to function properly as they would on a standard input field. This is the structure of my custom ...

Changes made to one order's information can impact the information of another order

Currently, I am in the process of developing a unique shopping cart feature where users input a number and a corresponding product is added to a display list. Users have the ability to adjust both the price and quantity of the products, with the total pric ...

Creating a digital collection using Vue, Typescript, and Webpack

A short while back, I made the decision to transform my Vue project into a library in order to make it easier to reuse the components across different projects. Following some guidelines, I successfully converted the project into a library. However, when ...

Leveraging TypeScript with Angular components

Situation: On my website, I have a section called the "main page" where all available books are displayed. The "main page" is represented by the blue box in the image provided and serves as a key component on my site. Additionally, there is a separate co ...

Navigating through nested routes in Angular 5

I recently started learning about Angular, and I could really use some guidance on routing. Here is my current setup. app.component.html <router-outlet name="nav"></router-outlet> <router-outlet name="left-sidebar"></router-outlet> ...

Facing an issue with the TypeScript error in the Tailwind-Styled-Component Npm package. Any suggestions on how to troub

module.styles.ts File import tw from "tailwind-styled-components"; export const Wrapper = tw.div` bg-green-500 `; export const Link = tw.a` text-blue-500 `; home.jsx File import React from "react"; import { Wrapper, Link } from &qu ...

Utilize the index of a for loop to manipulate an Angular string

When working with different objects and creating forms simultaneously, I've come across a challenge. My initial idea for handling the submission was to use the following code: <form (ngSubmit)="submitForm{{u}}()"> However, incorporating the in ...