2

In my data object, I need to push objects into an array called editions.

data() {
  return {
    editions: []
  }
}

To do this, I am dynamically creating a form based on some predetermined field names. Here's where the problem comes in. I can't get v-model to cooperate. I was expecting to do something like this:

<div v-for="n in parseInt(total_number_of_editions)">
  <div v-for="field in edition_fields">
    <input :type="field.type" v-model="editions[n][field.name]" />
  </div>
</div>

But that isn't working. I get a TypeError: _vm.editions[n] is undefined. The strange thing is that if I try this: v-model="editions[n]"... it works, but I don't have the property name. So I don't understand how editions[n] could be undefined. This is what I'm trying to end up with in the data object:

editions: [
  {
    name: "sample name",
    status: "good"
  },
  ... 
]

Can anyone advise on how to achieve this?

3
  • 1
    You could initialize your data according to the expected structure, so v-model could bind to existing properties. Commented Dec 7, 2019 at 21:36
  • @yuriy636 But how do I do that if I have an arbitrary number of array elements? Commented Dec 8, 2019 at 3:26
  • Use the edition_fields variable to create the proper editions property in your data object. Commented Dec 8, 2019 at 4:35

1 Answer 1

1

But that isn't working. I get a TypeError: _vm.editions[n] is undefined.

editions is initially an empty array, so editions[n] is undefined for all n. Vue is essentially doing this:

const editions = []
const n = 1
console.log(editions[n]) // => undefined

The strange thing is that if I try this: v-model="editions[n]"... it works

When you use editions[n] in v-model, you're essentially creating the array item at index n with a new value. Vue is doing something similar to this:

const editions = []
const n = 2
editions[n] = 'foo'
console.log(editions) // => [ undefined, undefined, "foo" ]

To fix the root problem, initialize editions with an object array, whose length is equal to total_number_of_editions:

const newObjArray = n => Array(n)             // create empty array of `n` items
                          .fill({})           // fill the empty holes
                          .map(x => ({...x})) // map the holes into new objects

this.editions = newObjArray(this.total_number_of_editions)

If total_number_of_editions could change dynamically, use a watcher on the variable, and update editions according to the new count.

const newObjArray = n => Array(n).fill({}).map(x => ({...x}))

new Vue({
  el: '#app',
  data() {
    const edition_fields = [
      { type: 'number', name: 'status' },
      { type: 'text', name: 'name' },
    ];
    
    return {
      total_number_of_editions: 5,
      editions: [],
      edition_fields
    }
  },
  watch: {
    total_number_of_editions: {
      handler(total_number_of_editions) {
        const count = parseInt(total_number_of_editions)

        if (count === this.editions.length) {
          // ignore
          
        } else if (count < this.editions.length) {
          this.editions.splice(count)

        } else {
          const newCount = count - this.editions.length
          this.editions.push(...newObjArray(newCount))
        }
      },
      immediate: true,
    }
  }
})
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>

<div id="app">
  <label>Number of editions
    <input type="number" min=0 v-model="total_number_of_editions">
  </label>

  <div><pre>total_number_of_editions={{total_number_of_editions}}
editions={{editions}}</pre></div>

  <fieldset v-for="n in parseInt(total_number_of_editions)" :key="n">
    <div v-for="field in edition_fields" :key="field.name+n">
      <label>{{field.name}}{{n-1}}
        <input :type="field.type" v-if="editions[n-1]" v-model="editions[n-1][field.name]" />
      </label>
    </div>
  </fieldset>
</div>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.