Angular 6 property binding not working correctly with multi-level object

lvaughan

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.

GCSDC

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.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

binding with property not working in Angular

Binding to a property of an object in XAML not working

DatePipe is not working correctly in Angular 6

Post object with multi level property to controller

Angular material select not binding correctly the object value

disabled property of button binding not working angular 4

Angular 7 @Input property binding is not working

dynamic property binding in a table in angular 6

Data Binding with [ngModel] not working Angular 6

JavaScript: Access object multi-level property using variable

How to detect that a multi-level object has an undefined or null property?

Angular @Input, binding object to same object but with different property names

Angular not binding correctly with Gridster

Multi-Level NavBar in Angular 6 Bootstrap 4

Angular 6 - Multi-level nested Reactive Form duplicates inputs

property binding not working with brackets

Exposed Bindable Property not binding correctly

Input text box binding failing to set but working on get of an Angular property

Angular Material inline calendar picker 'selected' property binding not working

Angular 2 - Safe Navigation operator not working in template property binding

Object Property named with ? not working inside map in Angular

How to assign multi level Json data array to single Angular object?

Angular 6: Observable async binding not working as expected after HttpErrorResponse

Angular 2 ngModel Not Binding Object Property Defined In Class Definition

Angular2/Typescript object and property binding to template

Binding a text input to a property in an observable object - Angular 2+

Angular controller is not binding service object property ng-repeat

Binding to a property of the object in XAML

multi level groupby with function on a property