[SalesForce] How to change value on object in LWC

I have the following need. I built a LWC to display a lightning-input for "field" object passed in from a parent LWC for a user to update.

@track field = { 'id' : 'some-id', 'value' : 'user value' };

I have built a simple getter and setter for my field var. I am able to initially set and get my field var. However, using an onchange handler, I am unable to update the field.value property for this object. I am using my value getter in my html file to view the value change on the fly but it does not appear to be doing that. The idea is that this will create an event and put the entire field into the event to pass back to the parent LWC. Does anybody know what is going on?

<!--html -->
<template>
<template if:true={field}>
    <lightning-card title={id}>
        <lightning-input type="text" label={id} value={value} onchange={handleValueChange}></lightning-input>
        <p>Value: {value}</p>
    </lightning-card>
</template>
//js
import { LightningElement, track, api } from 'lwc'; 
export default class FieldCmp extends LightningElement {
@track _field;

@api
get field() {
    return this._field;
}

set field(val) {
    this._field = val;
}

get id() {
    return this._field.id;
}

get value() {
    return this._field.value;
}

handleValueChange(evt) {
    this._field.value = evt.target.value;
}
}

Best Answer

The value comes from an @api value, so it is completely locked down. You need to copy the value into an unlocked object, which is the point of using a custom getter/setter:

set field(val) {
  this._field = {...val};
}

See the Spread syntax for how this works. Also, a Playground. Note that the parent must check the value of the component's field value, as the value it has in it's @track value won't be updated, since this is a copy.