0

I have a new app that I am making as a pet project that one of my components is behaving wrong.

I have a complex object that stores the information for D&D monsters. The component is for a option quantity changer with plus and minus buttons the increment and decrement the quantities.

When I use it for a tier 1 child (ie. monster.strength) it works correctly and will increment up to the maximum quantity, and down to the base value (but not below base) When I use it for a tier 2 child (ie. monster.speed.base) it will increment correctly, but it actually changes the basemonster value as well as the selectedmonster which prevents the decrement from working.

Here's the code showing how the objects are added to the document.

<option-quantity *ngIf="mod.location === 'base'"
     [max]="90"
     [step]="5"
     [costval]="mod.cost" 
     [baseval]="baseMonster[mod.type][mod.location]" 
     [(totalcost)]="selectedMonster.cost" 
     [(optval)]="selectedMonster[mod.type][mod.location]">
 </option-quantity>

And here is the components TS file

import { Component, Input, Output } from '@angular/core';
import { EventEmitter } from '@angular/core';

@Component({
  selector: 'option-quantity',
  templateUrl: './option-quantity.component.html',
  styleUrls: ['./option-quantity.component.css']
})
export class OptionQuantityComponent {
  @Output('optvalChange') emitter1: EventEmitter<number> = new EventEmitter<number>();
  @Output('totalcostChange') emitter2: EventEmitter<number> = new EventEmitter<number>();
  @Input('baseval') set setBaseVal(value) {
    this.base = value;
  }
  @Input('optval') set setOptValue(value) {
    this.count = value;
  }
  @Input('costval') set setCostValue(value) {
    this.cost = value;
  }
  @Input('totalcost') set setTotalCostValue(value) {
    this.totalcost = value;
  }
  @Input('step') set setStepValue(value) {
    this.step = value;
  }
  @Input('max') set setMaxValue(value) {
    this.max = value;
  }

  step = 1;
  max = 10;
  base = 0;
  count = 0;
  cost = 0;
  totalcost = 0;

  increment() {
    if (this.count < this.max) {
      this.count += this.step;
      this.totalcost += this.cost * this.step;
      this.emitter1.emit(this.count);
      this.emitter2.emit(this.totalcost);
    }
  }

  decrement() {
    if (this.count > this.base) {
      this.count -= this.step;
      this.totalcost -= this.cost * this.step;
      this.emitter1.emit(this.count);
      this.emitter2.emit(this.totalcost);
    }
  }

  onChange() {
    this.emitter2.emit(this.totalcost);
    this.emitter1.emit(this.count);
  }

}

I have verified the problem lies with the tier 2 child, since i tried moving the stats into a stats child, and the speed to the root. Which made the stats stop working and the speed work fine. I COULD just move the speed to the root of the object, but I'd rather not.

The component where the values are used is the create-undead component the baseMonster is created by this function:

  setBase() {
    this.baseMonster = Object.assign({}, this.selectedMonster);
    this.currentSize = this.baseMonster.size;
    this.previousSize = this.baseMonster.size;
  }

The entire project can be viewed in my GitHub repo

Updates: I've tried using Object.spread instead of assign, but that didn't make any difference. If I use Object.freeze and do a deep freeze on the "baseMonster" object, that object will not change, but then the "selectedMonster" stops having its tier 2 child values update.

Any help would be greatly appreciated.

Lee
  • 55
  • 8
  • I was in a long meeting and pulled down/ran your repo. I don't see any incrementor widgets of any kind, you just click an undead creature and see its stats. I assume there's a way to edit an existing, or create a new, which I'd need to see in order to troubleshoot (I think I know what the problem is but can't be sure). – Tim Consolazio Nov 27 '18 at 17:24
  • Yeah I saw the manage components, figuring one of them (monster form) would have the functionality you were talking about, I hooked them all up to a route and tried, they're all just blank. – Tim Consolazio Nov 27 '18 at 17:32
  • I tried undead creator too, you can select a base monster, and it displays, but there's no way to edit it. – Tim Consolazio Nov 27 '18 at 17:38
  • 1
    Yeah, the edit would be located under /create-undead or in the folder structure /creator/components/create-undead. There is an accordion which I use to separate the different modifiers out by type. The only one that seems to not work is the option-quantity component for the speed modifiers which load properties for baseval as baseMonster[mod.type][mod.location] and optval as selectedMonster[mod.type][mod.location] The site can be seen at https://undead-creator.firebaseapp.com/create-undead as well. – Lee Nov 27 '18 at 20:08
  • Ok.. sorry for being an idiot to those who tried to help earlier by looking at my repo.. apparently my editor was only doing commits, and not pushes. If you're curious you can see the updated code now. – Lee Nov 27 '18 at 23:33

1 Answers1

4

The issue is with the way you are doing your copy:

this.baseMonster = Object.assign({}, this.selectedMonster);

Object.assign won't do a deep copy of the object, as described here: "If the source value is a reference to an object, it only copies that reference value."

This answer has a simple approach for that:

clonedObj = JSON.parse(JSON.stringify(originalObj))

This other answer has a detailed explanation on the topic.

GCSDC
  • 3,138
  • 3
  • 28
  • 48
  • another option is to use lodash and the cloneDeep method – bryan60 Nov 27 '18 at 19:56
  • Thank you! This actually did what my other oddball attempts could not. Id tried Object.spread, but that had the same effect, when I ran it through a function to freeze each child object as well as the parent, that stopped the clone from changing, bug broke the original as well oddly enough. – Lee Nov 27 '18 at 20:28