-3

In vuecli I have data like this

data() {
        return {
            options: [{
                values: ['a', 'b', 'c']
            }],
            variants: [],
            p: {            
               option_1: null 
            }
        }
    }

and when i run a loop inside a method that looks like this

methods: {
  add() {
    for(let i = 0; i < this.options[0].values.length; i++) {

        (function(i, p){
            var raw = p;
            raw.option_1 = this.options[0].values[i]; 
            this.variants.push(raw); 
        })(i, this.p);

    } 
  }
}

I tried in many ways but succeed only when I set the value of raw inside the loop likevar raw = {option_1: null} .

But this is not what i want. I want to take values from data and use it in the loop to produce

variants: [{ option_1: 'a' }, { option_1: 'b' }, { option_1: 'c' }]

How can I accomplish this ??

Phil
  • 157,677
  • 23
  • 242
  • 245
SwadhIn
  • 717
  • 1
  • 9
  • 19
  • What is `self` in that function? Also, what is the point of this loop function? You overwrite `p.option_1` each iteration. Can you explain what you want the end state to be? – Phil May 24 '18 at 05:26
  • sorry self will be 'this'. I fixed it. I want to push 'option' values into variants array. But I am getting the value 'c' three times instead of 'a' 'b' 'c' – SwadhIn May 24 '18 at 05:37
  • variants: [{ option_1: 'a' }, { option_1: 'b' }, { option_1: 'c' }] but I am getting 'c' for three times – SwadhIn May 24 '18 at 05:40
  • What about if `add()` is called multiple times? – Phil May 24 '18 at 05:41
  • I think its something related to 'closure' but i cant figure out the solution – SwadhIn May 24 '18 at 05:43
  • Before for, have something like var self = this; And instead of using this inside self calling functions, use self. – bthe0 May 24 '18 at 05:45
  • Yes, your problem is somewhat related to this ~ [How to access the correct `this` inside a callback?](https://stackoverflow.com/q/20279484/283366). You also have an issue in that you are pushing the same object into your array and changing its property, thus they all have the same value – Phil May 24 '18 at 05:45

2 Answers2

1

You need a copy of raw because raw in variants is just a reference which point to the same object. That's why you got three same value.

add() {
  let self = this
  for (let i = 0; i < self.options[0].values.length; i++) {
    (function (i, p) {
      var raw = p;
      raw.option_1 = self.options[0].values[i];
      self.variants.push(JSON.parse(JSON.stringify(raw)));
    })(i, self.p);
  }
  // this.options[0].values.forEach(v => {
  //     this.variants.push({ option_1: v })
  // })
}

Code in the comment is a more elegant way.

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
  <mytag></mytag>
</div>
<script>
  let mytag = Vue.component("mytag", {
    template: `<div><button @click="add">add</button><p>this.variants:{{this.variants}}</p></div>`,
    data() {
      return {
        options: [{
          values: ["a", "b", "c"]
        }],
        variants: [],
        p: {
          option_1: null
        }
      };
    },
    methods: {
      add() {
        let self = this
        for (let i = 0; i < self.options[0].values.length; i++) {
          (function(i, p) {
            var raw = p;
            raw.option_1 = self.options[0].values[i];
            self.variants.push(Object.assign({}, raw));
            //self.variants.push(JSON.parse(JSON.stringify(raw)));
          })(i, self.p);
        }
        // this.options[0].values.forEach(v => {
        //     this.variants.push({ option_1: v })
        // })
      }
    }
  });
  new Vue({
    el: '#app',
    components: {
      mytag
    }
  })
</script>

In the end, you had better learn something about how to ask!

xianshenglu
  • 4,943
  • 3
  • 17
  • 34
  • This seems very over-complicated. There's really no need for the IIFE nor for the JSON parse / stringify. A for loop with `this.variants.push({option_1: this.options[o].values[i]})` is all you need – Phil May 24 '18 at 05:58
  • There is a more elegant way in the comments. And which part in the comments you think is over-complicated ?@Phil – xianshenglu May 24 '18 at 06:00
  • Yes, your `forEach` is much cleaner though it omits any extra properties that `p` might have – Phil May 24 '18 at 06:07
  • @Phil,yeah,it depends whether OP needs `p` or not. – xianshenglu May 24 '18 at 06:12
0

If you want the end result to look like

variants: [{
  option_1: 'a'
}, {
  option_1: 'b'
}, {
  option_1: 'c'
}]

Where each entry is templated by p with option_1 set to each values entry, you can use

this.variants = this.options[0].values.map(option_1 => ({...this.p, option_1 }))

This maps the values to an array of objects with key option_1 and the value from each values item.


If you want to add the 3 objects each time you call add(), change it to use Array.prototype.concat()

this.variants = this.variants.concat(
    this.options[0].values.map(option_1 => ({...this.p, option_1 })))
Phil
  • 157,677
  • 23
  • 242
  • 245