0

So I created a base class called AncientWisdom... and sub classes based on 15 different AncientWisdoms with more specific detail. My problem is that on angular I output each of the PermanentUpgrade... classes out and when I do an end value that triggers the > maxUnlocks logic it will update all subclasses that are inheriting from AncientWisdom to that value as well as throw a model value being updated unexpectedly. I've tried moving the if statement even down to the class level and still getting the error.

I will probably end up just going instead with only the base class and setting a few values, but I'm very confused on why my classes are sharing variables like that 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>
1
  • On a side note, I switched to just using the AncientWisdom class, taking in name, costModifer, and maxUnlocks and created multiple instances. Get the same issue when hitting the logic that changes the desired to equal max unlocks. Changes all instances on the form. Commented Oct 16, 2018 at 3:16

2 Answers 2

1

This is due to Angular not tracking changes to the array properly. Provide a trackbyFn:

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

In the view:

<div *ngFor="let ancientWisdom of ancientWisdoms;trackBy: trackByFn" ...>
Sign up to request clarification or add additional context in comments.

Comments

0

I found that if I remove the FORM element tag from my HTML, it seems to resolve the issue. It started only changing the appropriate element. I think it was because it was one form tag and they all had the same name for the input. It did something weird to the bindings.

However there was still the issue of

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

I found a list of solutions for this problem here https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

But I went with the following solution

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

Changing it to a promise instead of a direct change allowed it to finish the cycle and update it on a normal angular cycle. There was also a suggestion of using setInterval but I didn't like that idea.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.