What limitations do we face when trying to change the objects received by a component through @input() in Angular?

Recently, I made the leap from angular 7 to angular 11 in my app. Everything was running smoothly until I decided to incorporate angular universal for server-side rendering.

Shortly after implementing server-side rendering, a flurry of errors cropped up, all stating "Can't change readonly 'xyz' member of object [Object Object]". These members belong to an object passed from the parent component to the child component using @Input()

Now, I have some burning questions:

  1. Is it considered bad practice to manipulate objects that are passed as Input?
  2. Why does this issue arise specifically with Angular universal (server-side rendering) and not with client-side rendering?

Here's an example of one such component:

export class BannerComponent {

  @Input() banners : Offer[]
  
  constructor(private analyticService : AnalyticService) { }

  ngOnChanges() {
    if(this.banners) {
      this.banners.forEach(banner => {
        if(!banner.bannerImage.startsWith("http"))
          banner.bannerImage = environment.imageHost + banner.bannerImage;
      })
    }    
  }

  recordEvent(banner : Offer) {
    this.analyticService.eventEmitter(banner.category.name, "Click on banner", banner.offerDetail + "-" + banner.merchant.name, AnalyticService.AVG_AFFILIATE_CLICK_VALUE);
  }

}

And here is my offer class:

import { Store } from "./store";
import { Category } from "./category";

export class Offer {
    
    id: number;
   
    merchant: Store;
   
    offerDetail: string;
   
    link: string;
   
    openExternal: boolean;
   
    logoPath: string;
   
    lastDate: Date;
   
    banner: boolean;
   
    bannerImage: string;

    category : Category;
    offerDescription?: string;
}

Additionally, there are two other models - Store and Category.

Answer №1

On the topic of question 1:

Avoiding data manipulation from the parent component in child components is advisable.

In Angular, data moves from parent to child components in a unidirectional flow, as referenced in the angular documentation.

Your situation involves altering the data of the parent component.

Here's an example to consider:

@Component({
   template: `<app-banner-component [banners]="parentBanners"></app-banner-component>`
})
class ParentComponent {
    parentBanners: Offer[] = {...};
}

When ngOnChanges is triggered, changes made to the bannerImage in the parentBanners will affect both the parent and child components due to passing by reference.

This can result in the ExpressionChangedAfterItHasBeenChecked error due to change detection running from parent to child components, as mentioned in this video.

To address this issue, it is recommended to create a copy of the array, like so:

@Input() set banners (values: Offer[]) {
    this._banners = values.map(offer => ({...offer}));
}
get banners(): Offer[] {
    return this._banners;
}

private _banners : Offer[];

In regard to question 2, I lack experience with Angular Universal. It is possible that stricter data flow enforcement by Angular Universal may require refactoring to resolve any issues.

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

Is it possible to safely change the name of an Angular project?

Looking to completely rename an Angular 8 project from top to bottom - changing the folder name and every line of code that references the original project name. I have seen in past posts (and previous versions of Angular) that there was no CLI command fo ...

I'm receiving an error message stating "mongoose.connect is not a function" while attempting to establish a connection with mongoose. Can you help me troub

I'm a beginner in Node.js and I'm currently working on creating a node/express/mongoose server app using TypeScript. Below is my app.ts file: // lib/app.ts import express from 'express'; import * as bodyParser from 'body-parser&a ...

When I click on a tab section to expand it, the carat arrows all point upwards. Only the arrows corresponding to the selected section should

click here for imageIt appears that there are four tabs, each with a click function on the carat icon. When I expand one tab, all carats point upwards instead of only the selected one appearing. accountSelection(account) { if (!this.selectedAccoun ...

Tips on how to exclude one key from validation and ensure that all other keys have a non-empty value

Currently, I am learning about array functions in JavaScript and have come across a solution that involves using Object.fromEntries. However, the dilemma is that in my Angular project, I am constrained by an outdated ES version which cannot be updated due ...

"Encountering a Problem with Rendering the Datetime Picker Component in Angular

When using material-components/[email protected] with angular 14, I encountered an issue where the calendar popup sometimes renders out of place (see image below). Initially, I suspected it to be a cache problem and tried refreshing the page, which te ...

Is there a way to verify if a value is undefined before including it as an object field?

I'm currently working on an Angular project and I have a query regarding TypeScript. It's about correctly handling the scenario where a field should not be included in an object if its value is undefined. In my code, I am initializing an object ...

Guide to importing a scss file into a scss class

Is there a way to apply a different theme by adding the "dark-theme" class to the body? I've attempted the following implementation: @import '../../../../node_modules/angular-grids/styles/material.scss'; .app-dark { @import '../../. ...

Tips for maintaining consistent column width when scrolling down (column juggling) within a table in your Angular application

Take a look at this preview of the app I'm currently developing. https://stackblitz.com/edit/angular-ivy-3mrzkr In order to handle the large amount of data in my real app, I needed to incorporate a third-party scrolling module. After testing out cdk ...

Different approaches for implementing custom type guards?

I've been studying User-Defined Type Guards in the Typescript handbook. Imagine you have a union type that needs to be narrowed down like this: interface Bird{ fly(); layEggs(); } interface Fish{ swim(); layEggs(); } class SmallPet ...

Tips for utilizing single quotation marks while logging multiple variables in console

When I write console.log("entered values are "+A+" and "+B); the tsLint gives a warning that single quotes should be used. However, I discovered that if I use single quotes, I am unable to include multiple variables in the same console ...

Angular 2: Simplifying the Process of Retrieving a Full Address Using Latitude and Longitude

Currently, I am utilizing the angular 2-google-maps plugin. Is there a way to retrieve the country and postal code based on latitude and longitude using Angular 2 Google Maps with Typescript? ...

Jest test encounters an error due to an unexpected token, looking for a semicolon

I've been working on a Node project that utilizes Typescript and Jest. Here's the current project structure I have: https://i.stack.imgur.com/TFgdQ.png Along with this tsconfig.json file "compilerOptions": { "target": "ES2017", "modu ...

Storing checkbox status in Angular 7 with local storage

I am looking for a way to keep checkboxes checked even after the page is refreshed. My current approach involves storing the checked values in local storage, but I am unsure of how to maintain the checkbox status in angular 7 .html <div *ngFor="let i ...

I'm wondering why my JWT token appears as null on the backend while it is not null on the frontend

I'm having trouble with a GET request to my mLab database. I included a JWT token in the request and verified it on both the client and server side. Strangely, it appears correctly on the client but as null on the server. Any assistance on this matter ...

Looking to grab a single value from a JSON file and utilize it in component code within Angular 2 (8.2.8)?

My JSON file contains information about various pages: { "pagesList": [ { "pageUrl": "index", "imgUrl": "homepage", "imgNumber": 17 }, { "pageUrl": "second", "imgUrl": "secondimage", ...

Customize the style of Angular Material within an Angular component

In my Angular component, I am utilizing Material 2's <md-input-container>. I am looking to customize a specific class, such as .mat-input-wrapper, that is originally defined in Angular Material. However, my intention is for this customization to ...

Informing ng2-bootstrap's Timepicker of the invalidation

I have integrated ng2-bootstrap's timepicker component into my project. To enhance user experience, I created a custom validation function that is triggered by the ngModelChange() event. However, the timepicker component also comes with its own built- ...

Leverage JSON files for pagination in NextJS

I am currently developing a science website where the post URLs are stored in a static JSON file. ScienceTopics.json- [ { "Subject": "Mathematics", "chapters": "mathematics", "contentList": [ ...

Encountering a Validation Error while Creating a Progressive Web App using Ionic CLI

I'm trying to build a simple PWA app using the IONIC Framework, but have been struggling to make it work. I am still new to Ionic and have followed various tutorials from different sources without success. If you want to check out the tutorials I&apo ...

Encountering a reload error while refreshing the Angular page

Whenever I click on a deck from my list, the corresponding deck-detail component is supposed to load and display the details of the selected deck. The URL should also change to something like "deck/id/deckName". However, if I try to reload the page or copy ...