Vue 3 using v-model as props instead of :prop and @emit
Asked Answered
A

1

6

So I've read this article a couple of times and as I understand, I can use v-model, instead of props, to pass values from a parent to a child component and automatically emit event when prop's value is modified in a child, thus getting two-way binding with less code (no need to catch emitted events in a parent). However it doesn't work the way I think it should.

Here is my code:

<template>
  <!-- This is ParentComponent.vue -->
  <ChildComponent v-model:documents="user.documents" />
</template>
<script lang="ts">
  // This is ParentComponent.vue
  import { Vue, Options } from 'vue-class-component';
  import UserClass from '@/some/place/UserClass';
  import ChildComponent from '@/components/ChildComponent.vue';

  @Options({
    components: {
      ChildComponent,
    }
  })
  export default class ParentComponent extends Vue {
    // Local state.
    user: UserClass = new UserClass();
  }
</script>
<template>
  <!-- This is ChildComponent.vue -->
  <section v-for="document in documents" :key="document.id">
    {{ document.name }}
    <button @click="remove(document.id)">Delete document</button>
  </section>
</template>
<script lang="ts">
  // This is ChildComponent.vue
  import { Vue, Options } from 'vue-class-component';
  import IDocument from '@/interfaces/IDocument';

  @Options({
    props: ['documents'],
    emits: ['update:documents'],
  })
  export default class ChildComponent extends Vue {
    // Types.
    documents!: IDocument[];

    // Methods.
    remove(documentId: string): void {
      this.documents = this.documents.filter((document) => document.id !== documentId);
    }
  }
</script>

I expect that when one clicks a button inside a child component, it should trigger a "remove()" method, and instead of direct assignment of a new value to this.documents, it should emit the update:documents event, which in turn should be caught by a parent component and should be used to update parent component's local state.

But instead, I get the following warning:

Attempting to mutate prop "documents". Props are readonly.

And the following error:

Uncaught TypeError: proxy set handler returned false for property '"documents"'

Where am I wrong? Thanks in advance.

Achromatism answered 19/6, 2021 at 18:38 Comment(0)
A
6

I guess I've missed an important feature of how v-model works. I've assumed that passing value using v-model like this

<ChildComponent v-model:documents="user.documents" />

would automatically emit update:documents event, when the value of documents prop is changed in the ChildComponent. But it seems like you still need to emit this event manually as follows:

<script lang="ts">
  // This is ChildComponent.vue
  import { Vue, Options } from 'vue-class-component';
  import IDocument from '@/interfaces/IDocument';

  @Options({
    props: ['documents'],
    emits: ['update:documents'],
  })
  export default class ChildComponent extends Vue {
    // Types.
    documents!: IDocument[];

    // Methods.
    remove(documentId: string): void {
      // The user.documents in the ParentComponent would be overriden with filtered array, generated here in the child.
      this.$emit('update:documents',  this.documents.filter((document) => document.id !== documentId));
    }
  }
</script>
Achromatism answered 20/6, 2021 at 11:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.