[Vuejs]-Vue 3 v-model binding not working when passing filtered data

1๐Ÿ‘

OK. Adding the event listener @update:user and handling the update manually (using a custom method) solves the issue.

But if I bind directly to the properties of the user object, as bellow, then the users array (in the parent) gets updated properly, even though Iโ€™m using the filteredUsers property.

<template>
  <div>
    <User 
      v-for="(user, index) in filteredUsers"
      :key="index"
      v-model:first-name="filteredUsers[index].firstName"
      v-model:last-name="filteredUsers[index].lastName"
    ></User>
  </div>
</template>

<script>
import User from '../components/User.vue'

export default {

  components: {
    User
  },
  data() {
    return {
      users: [
        {
          firstName: 'jean',
          lastName: 'paul',
        },
        {
          firstName: 'pierre',
          lastName: 'smith',
        },
        {
          firstName: 'jean',
          lastName: 'valjean',
        }
      ],
    }
  },

  computed: {
    filteredUsers() {
      return this.users.filter(user => user.firstName.includes('jean'))
    }
  },

  watch:{
    users: {
      handler(newVal, oldVal) {
        console.log('users changed')
      },
      deep: true
    }
  }
}

</script>
// Child User.vue
<script>

export default {
  props: {
    firstName: String,
    lastName: String,
  },
  emits: [
    'update:firstName', 
    'update:lastName',
  ],
}
</script>

<template>
  <div>
    <input
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)"
    />
    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)"
    />
  </div>
</template>

Why does it work in such case and not when passing the object as a prop (v-model:user="filteredUsers[index]") ? (as in my first question)

๐Ÿ‘คskuallpa

0๐Ÿ‘

You are processing all the data based on a computed property which is producing the results based on users array as a "new object". You have to find the array index and then update it. Review my code below:
I have only changed the Parent component, integrated the event listener and a function that processes it. Happy Coding!

// Parent component
<template>
  <div>
    <User v-for="(user, index) in filteredUsers" @update:user="updateUser" :key="index"
      v-model:user="filteredUsers[index]"></User>
  </div>
</template>

<script>
import _ from 'lodash'
import User from './components/User.vue'

export default {

  components: {
    User
  },
  data() {
    return {
      users: [
        {
          id: 1, //we are using it to identify user data
          firstName: 'jean',
          lastName: 'paul',
        },
        {
          id: 2,
          firstName: 'pierre',
          lastName: 'smith',
        },
        {
          id: 3,
          firstName: 'jean',
          lastName: 'valjean',
        }
      ],
    }
  },

  computed: {
    filteredUsers() {
      // Problem: When filtering the users, users is no longer getting updated when editing the user in the child component.
      return this.users.filter(user => user.firstName.includes('jean'))

      // If not filtering, then users is well updated and the watcher notice the change.
      // return this.users
    }
  },
  methods: {
    updateUser(user) {

      let uIdx = _.findIndex(this.users, function (u) { return u.id == user.id; });
      console.log('user', user, uIdx);
      if (this.users[uIdx]) {
        this.users[uIdx] = user;
      }

    }
  },
  watch: {
    users: {
      handler(newVal, oldVal) {
        console.log('users changed')
      },
      deep: true
    }
  }
}

</script>

Another method which can work without "$emit"

//Parent Component
<template>
  <div>
    {{ users }}
    <hr>{{ filteredUsers }}
    <User v-for="(user, index) in filteredUsers" :key="index" v-model:user="filteredUsers[index]"></User>
  </div>
</template>

<script>
import User from './components/User.vue'

export default {

  components: {
    User
  },
  data() {
    return {
      users: [
        {
          firstName: 'jean',
          lastName: 'paul',
        },
        {
          firstName: 'pierre',
          lastName: 'smith',
        },
        {
          firstName: 'jean',
          lastName: 'valjean',
        }
      ],
    }
  },

  computed: {
    filteredUsers() {
      return this.users.filter(user => user.firstName.includes('jean'))
    }
  },

  watch: {
    users: {
      handler(newVal, oldVal) {
        console.log('users changed')
      },
      deep: true
    }
  }
}

</script>

We are not using emit in child component

// Child User.vue
<script>

export default {
    props: {

        user: Object
    }

}
</script>

<template>
    <div>

        <input type="text" v-model="user.firstName" />
        <input type="text" v-model="user.lastName" />
    </div>
</template>
๐Ÿ‘คieatbytes

Leave a comment