ChibiNya
ChibiNya

Reputation: 133

How to pass editable data to component?

I'm working on an app that allows you to capture and edit soccer match results.

there is a Matches component that makes an AP call to get the data of multiple matches from a server (match_list) and then renders a bunch of Match components, passing the data as props to these sub-components to fill their initial values.

<component :is="Match" v-for="match in match_list"
                     v-bind:key="match.id"
            v-bind="match"></component>

On the Match component, I accept all the values as props. But I get a warning that props shouldn't be edited and these should be data elements instead. So I tried passing them to the component data.

export default {
        name: "Match",
        props: ['local_team', 'visitor_team', 'localScore', 'visitorScore', 'date', 'time', 'location', 'matchId'],
      data(){
          return{
            id: this.id,
            local_team: this.local_team,
            visitor_team: this.visitor_team,
            location: this.location,
            date: this.date,
            time: this.time,
            localScore: this.localScore,
            visitorScore: this.visitorScore
          }
      },

Now I get a warning that editable data shouldn't be based on props.

How can I make the data from the Match component editable so it safely propagates to the parent component?

Upvotes: 0

Views: 620

Answers (1)

Amr Noman
Amr Noman

Reputation: 2637

You need to accept your match object on the component's props, and make a copy of it on data (to be used as a model for your inputs). When your model changes you should emit that change to the parent so that it can change its own data appropriately (which then gets passed and reflected correctly through the child's props):

In this example I watch for any changes to the model and then emit the event directly, you can of course replace that behavior by having a submit button that fires the event upon click or something.

Vue.component('match', {
  template: `
    <div>
      <p>{{match.name}}</p>
      <input v-model="matchModel.name" />
    </div>
  `,
  props: ['match'],
  data() {
    return {
      matchModel: Object.assign({}, this.match)
    }
  },
  watch: {
    matchModel: {
      handler(val) {
        this.$emit('match-change', val)
      },
      deep: true,
    }
  }
});


new Vue({
  el: "#app",
  data: {
    matches: [{
        id: 1,
        name: 'first match'
      },
      {
        id: 2,
        name: 'second match'
      }
    ]
  },
  methods: {
    onMatchChange(id, newMatch) {
      const match = this.matches.find((m) => m.id == id);
      Object.assign(match, newMatch);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
  <match v-for="match in matches" :match="match" :key="match.id" @match-change="onMatchChange(match.id, $event)"></match>
</div>

Upvotes: 1

Related Questions