Troubleshooting Problems with Angular 6 Inheritance

After creating a base class named AncientWisdom and multiple subclasses representing different aspects of ancient wisdom, I encountered an issue in Angular. When the end value triggers the logic for exceeding the maximum unlocks, all subclasses inheriting from AncientWisdom are updated to that value, causing unexpected changes in model values. Despite trying various solutions, including moving the if statement down to the class level, the error persists.

To resolve this confusion, I may opt to work with only the base class and set specific values. However, I am puzzled as to why my classes share variables without being static.

export class AncientWisdom {
    startValue: number;
    endValue: number;
    name: string;
    maxUnlocks: number;

    constructor() {
        this.endValue = 0;
        this.startValue = 0;
        this.maxUnlocks = -1;
    }

    calculateCost(): number {
        if (this.endValue > this.maxUnlocks) {
            this.endValue = this.maxUnlocks;
        }

        const currentCost = this.startValue * (this.startValue + 1);
        const desiredCost = this.endValue * (this.endValue + 1);
        const cost = (desiredCost - currentCost) * 400 / 2;
        return cost > 0 ? cost : 0;
    }
}

import { AncientWisdom } from "./ancientWisdom.model";

export class PermanentUpgradeEnergy extends AncientWisdom {
    constructor() {
        super();

        this.name = 'Increased Energy Points';
        this.maxUnlocks = 2;
    }

    calculateCost(): number {
        const currentCost = this.startValue * (this.startValue + 1);
        const desiredCost = this.endValue * (this.endValue + 1);
        const cost = (desiredCost - currentCost) * 400 / 2;
        return cost > 0 ? cost : 0;
    }
}

import { AncientWisdom } from "./ancientWisdom.model";

export class PermanentUpgradeLessHP extends AncientWisdom {
    constructor() {
        super();

        this.name = 'Reduced Boss HP';
        this.maxUnlocks = 10;
    }

    calculateCost(): number {
        const currentCost = this.startValue * (this.startValue + 1);
        const desiredCost = this.endValue * (this.endValue + 1);
        const cost = (desiredCost - currentCost) * 200 / 2;
        return cost > 0 ? cost : 0;
    }
}

export class AncientWisdomsComponent implements OnInit {

    ancientWisdoms: AncientWisdom[] = [];

    constructor() { }

    ngOnInit() {
        this.ancientWisdoms = [
            new PermanentUpgradeMoreXP,
            new PermanentUpgradeMoreGold,
            new PermanentUpgradeMoreDrops,
            new PermanentUpgradeMoreMovementSpeed,
            new PermanentUpgradeLessHP,
            new PermanentUpgradeEnergy,
            new PermanentUpgradeMoreEnemies,
            new PermanentUpgradeLongerBuffs,
            new PermanentUpgradeMoreMercenaries
        ];
    }
}

<h1 class="mt-5">Ancient Wisdoms</h1>
<form>
    <div *ngFor="let ancientWisdom of ancientWisdoms" class="form-group row">
        <div class="col-3">
            <label class="form-label">{{ancientWisdom.name}}</label>
        </div>
        <div class="col">
            <input type="number" class="form-control" name="startValue" [(ngModel)]="ancientWisdom.startValue" />
        </div>
        <div class="col">
            <input type="number" class="form-control" name="endValue" [(ngModel)]="ancientWisdom.endValue" />
        </div>
        <div class="col">
            {{ancientWisdom.calculateCost()}}
        </div>
    </div>
</form>

Answer №1

The reason for this issue is Angular's inability to properly monitor changes in the array. You can solve this problem by implementing a trackbyFn function:

  trackByFn(index, item) {
    return index; // or any unique attribute
  }

In your template:

<div *ngFor="let ancientKnowledge of ancientKnowledges;trackBy: trackByFn" ...>

Answer №2

After some experimentation, I discovered that by removing the FORM element tag from my HTML code, the issue was resolved. This change ensured that only the appropriate elements were being modified. It seemed that having multiple form tags with the same input name caused a conflict in the bindings.

Despite this improvement, I still encountered an error:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.

In my quest for a solution, I came across various suggestions on how to address this issue at this resource. However, I opted for the following approach:

    if (this.maxUnlocks > 0 && this.desiredValue > this.maxUnlocks) {
        Promise.resolve(null).then(() => this.desiredValue = this.maxUnlocks);
        return;
    }

By converting the direct change into a promise, I allowed the update to occur within a normal angular cycle without disruptions. While there was a suggestion to use setInterval as well, I personally preferred the promise-based solution.

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 sets apart the symbols '' and "" in TypeScript syntax?

As I work with TypeScript in Angular, I have noticed that sometimes we use single quotes like ' ' and other times we use double quotes like " ". Can you explain the difference to me? ...

Ensure that the type of the value passed to the callback function is enforced within the callback function in the

One of my jQuery plugins has a prompt function that accepts a callback function with setPrompt as the only parameter: Here is an example of how the code looks: obj.prompt(function(setPrompt) { setPrompt(10); }); I am wondering if it is possible to en ...

Calculate the sum of elements within an array

I've been attempting to calculate the sum of values in an array based on different levels, but so far I haven't had much success. Here are the data I'm working with (stored in a variable called totalByLevel): https://i.stack.imgur.com/rOP9 ...

configure the environment variable NODE_ENV in a NodeJS application

I am trying to properly set process.env.NODE_ENV to 'production' in my local NestJS code. Here are the steps I have attempted: Added NODE_ENV=production to the serve script in package.json Added nx build --prod to the build script in package.jso ...

Error encountered during the deployment of Ionic 3 with TypeScript

After completing the development of my app, I ran it on ionic serve without any issues. However, when compiling the app, I encountered the following error message. Any assistance in resolving this matter would be greatly appreciated. [15:40:08] typescri ...

Retrieve the final variable in an Observable sequence

In my code, I have a variable called 'messages' which stores messages from a conversation: messages: Observable<Message[]>; To populate the 'messages' variable, I do the following: const newMessage = new Message(objMessage); ne ...

Establish a table containing rows derived from an object

I am currently dealing with a challenge in creating a table that contains an array of nested objects. The array I have follows this schema: array = [ { id: 'Column1', rows: { row1: 'A', row2 ...

incomplete constructor for a generic class

I have multiple classes that I would like to initialize using the following syntax: class A { b: number = 1 constructor(initializer?: Partial<A>) { Object.assign(this, initializer) } } new A({b: 2}) It seems to me that this ini ...

Retrieving the data from a observable

Hello, I am currently learning Typescript/Angular, so please excuse me if this question has already been asked (I couldn't find it) or if it has a simple solution :) My goal is to have a timer displayed on my webpage. I have implemented it in my comp ...

Issue encountered while integrating the angular-fontawesome library in StackBlitz

After trying to add a library, an error keeps popping up on stackblitz. The error message in ~/src/main.ts states that ngcc failed to run on @fortawesome/[email protected]. Link to the Stackblitz project Is anyone else experiencing this issue? ...

The material UI style is not being implemented properly in the final production or build

While applying styles to the ListItemButton component from MUI by targeting the specific class .css-10hburv-MuiTypography-root, it works fine in development but not in production. I have tried various methods, including directly applying the styles on th ...

The dropdown for selecting months and years is not appearing on Angular 13's ngbdatepicker

Currently, I am developing a project in Angular 13 with Bootstrap 4. I am trying to implement a datePicker using ngbDatepicker, but unfortunately, the datePicker is not displaying the dropdowns for selecting months and years at the top. Here is the code sn ...

What are the steps to implement IndexedDB in an Angular application?

I'm searching for a solution to utilize indexedDB within Angular. I need assistance with implementing data recovery or potentially using a browser-based database that doesn't have the 5 MB limit like localStorage. Can anyone point me in the right ...

How can I prevent buttons from interfering with an onPaste event?

I've created an image modal that allows users to upload or paste an image. Everything is working well, except for the fact that the buttons on the modal are capturing the focus. This means that pasting only works if the user manually clicks outside th ...

The issue lies within typescript due to process.env.PORT being undefined

I am a newcomer to working with TypeScript. Even after importing and using the dotenv package, I am still encountering issues with getting undefined values. Do I need to declare an interface for the dotenv variables? import express,{Application} from &apo ...

Struggling to retrieve posted data using Angular with asp.net

I have encountered an issue while sending a post request from Angular to my ASP.NET server. I am trying to access the values of my custom model class (SchoolModel) and I can see that all the values are correct inside Angular. However, when I attempt to ret ...

Ensuring correct association of values to avoid redundancies

There are 5 fields available for users to fill out on this form: Leave Code, From Date, Input Time1, To Date, and Input Time2. These variables are declared as a dates object in the .ts file, as shown below. interface Supervisor { name: string; code: s ...

Is there a way to retrieve the quantity of children from an element using protractor?

I am currently working with Protractor and I need to determine the amount of child components associated with a specific element. The element in question belongs to a table category. let table = element(by.css('#myTable')); My objective now is ...

What steps can I take to guarantee that the observer receives the latest value immediately upon subscribing?

In my Angular 2 and Typescript project, I am utilizing rxjs. The goal is to share a common web-resource (referred to as a "project" in the app) among multiple components. To achieve this, I implemented a service that provides an observable to be shared by ...

Angular 2 is showing an error message: TS1005 - It is expecting a comma

Upon compiling, I encountered the following error: I am unable to locate where a comma needs to be inserted. src/app/navbar.component.ts(29,39): error TS1005: ',' expected. src/app/tache.service.ts(53,53): error TS1005: ',' expected. ...