1

I have and EditTransaction component and calling it just like this:

<edit-transaction
    v-if="editableTransaction.id === transaction.id"
    :key="'transaction'+transaction.id+etcount"
    :class="{'bg-red-300': type === 'expense', 'bg-green-300': type === 'income'}"
    :groups-prop="modelValue"
    :transaction="transaction"
    class="planning-transactions-item px-10 rounded-2xl w-[90%]"
    @close="editableTransaction = {id: null}">
</edit-transaction>

As you can see, I'm sending a transaction object in it. Since this is an editor, I don't want the transaction object to be reactive. If somebody closes the editor, I want to have the original transaction object and not some modified one, so if I'm correct and want to remove the proxy I put this in the editor:

const form = toRaw(props.transaction)

And inside the editor template, there are some asset components which v-model values bind to the form object

<div class="flex gap-5 w-full">
    <FormInput id="et-date" v-model="form.due_date" class="et-fields tw-fields w-[150px]"
               placeholder="Date"
               type="date"
               @keyup.enter="saveChanges"></FormInput>
    <FormInput id="et-name" v-model="form.name" class="et-fields tw-fields" placeholder="Name"
               @keyup.enter="saveChanges"></FormInput>
    <FormInput id="et-value" v-model="form.value" class="et-fields tw-fields" mask-thousand
               placeholder="Value"
               @keyup.enter="saveChanges"></FormInput>
</div>

The problem is when I'm changing like the transaction name for example, the form object changes but also the transaction prop. Therefore the name changes also in the parent data because the transaction prop is reactive. What am I doing wrong or how can I achieve to have a form object which values got filled up upon component create with the props value and does not have any proxies?

enter image description here

treb
  • 173
  • 2
  • 10
  • It's a common case. Transaction object needs to be cloned. – Estus Flask Mar 13 '22 at 06:54
  • Yes, but did I not do that by declaring the form object? – treb Mar 13 '22 at 08:35
  • There's no code in the OP that could be qualified as cloning. You assigned an object by reference. Check https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript – Estus Flask Mar 13 '22 at 12:52

2 Answers2

2

So I found two solutions for this:

const form = reactive({...props.transaction})

or

const form = Object.assign({}, props.transaction)

Both works and when I change the form value, it won't mutate the prop.

treb
  • 173
  • 2
  • 10
0

It's very common to use props to pass initial values to child component's state. This means that you "copy" the value of a prop in the local data. It keeps the prop value safe of unexpected changes: Read more in Vue docs

This is a pretty simple example showing the above approach:

/your-child-component-vue/

export default {
  props: ['initialCounter'],
  data() {
    return {
      // counter only uses this.initialCounter as the initial value;
      // it is disconnected from future prop updates.
      counter: this.initialCounter
    }
  }
}

Now, reading your example, I see that you're trying to update some data in a form, and you don't want to change the initial information unless it's confirmed by a button or something. The flow to approach this would be:

  • Pass the initial data that may be changed by the user as a prop.
  • If the user changed some data through inputs elements but not confirmed those changes (by a button) keep the data untouched (this means that you would not emit any changes to the parent, keeping the prop value untouched)
  • If user has changed some data and also confirmed it, emit this updated data to the parent (this.$emit) so It knows about the changes.
Lucas David Ferrero
  • 1,630
  • 14
  • 40
  • The OP uses composition API, and the prop is an object, so it may be unclear how simple example translates to the case – Estus Flask Mar 13 '22 at 06:53
  • Hi. You're right. How should I proceed? Should I delete this answer or leave it as it is? Thanks. – Lucas David Ferrero Mar 13 '22 at 21:58
  • Considering that the OP seems to solve it, looks like a good idea to keep the post in case somebody will need it and probably mention that it's an example for Vue 2. – Estus Flask Mar 13 '22 at 22:43