0

Every time I add an element it automatically replaces all the others but this happens to me only if I insert the whole object if I insert a classic element an integer a string works without problems.

 <body>
    <div id="app">
      <button @click="post()">POST</button>
      <h1>{{ar.name}} {{ar.surname}} {{ar.gender}}</h1>
      <br />
      <hr />
      <div v-for="(value, index) in array">
        <text>{{index}} {{value}} </text>
      </div>
    </div>

    <script src="https://unpkg.com/vue@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
      let app = Vue.createApp({
        data: function () {
          return {
            ar: {
              name: "",
              surname: "",
              gender: "",
            },
            array: [],
          };
        },
        methods: {
          async post() {
            await axios
              .get("https://randomuser.me/api/")
              .then((r) => {
                arr = r.data.results;
                arr.forEach((element) => {
                  this.ar.name = element.name.first;
                  this.ar.surname = element.name.last;
                  this.ar.gender = element.gender;
                  this.array.push(this.ar);
                });
              })
              .catch((err) => {
                console.log("Error " + err);
              });
          },
        },
      });
      app.mount("#app");
    </script>
  </body>
</html>

first element insert

second element insert

  • Because `this.ar` refers the same object in every iteration of the `forEach` loop, so you're adding pointers to that reference on every iteration. – Heretic Monkey May 22 '22 at 16:13
  • 1
    Does this answer your question? [Why does changing an Array in JavaScript affect copies of the array?](https://stackoverflow.com/questions/6612385/why-does-changing-an-array-in-javascript-affect-copies-of-the-array) – Heretic Monkey May 22 '22 at 16:13
  • but this.ar doesn't change pointer on every iteration? and why are all the values updated? – Andrea Petrelli May 22 '22 at 16:23
  • Right, it doesn't change pointer, so you're adding the same pointer to multiple elements in the array. When you change one, it changes to object that is pointed to, which is the same object in all slots of the array. – Heretic Monkey May 22 '22 at 16:27
  • Ah ok, how can I go about adding a different element each time without it overwhelming me all the others? – Andrea Petrelli May 22 '22 at 16:28
  • That's what the duplicate tells you. And basic JavaScript; create a new object in every iteration. You'll likely need to change something about your Vue... I don't know about that. – Heretic Monkey May 22 '22 at 16:32
  • What do you expect your `ar` data to hold? The last fetched element? – Kapcash May 22 '22 at 16:34

2 Answers2

2

You have a pointer reference issue. You actually add the same object pointer several times to the array, i.e. the same object reference:

Explaination of the problem

const obj = { name: 'ref1' };

const array = [];
array.push(obj);
array.push(obj);
array.push(obj);

// array === [{ name: 'ref1' }, { name: 'ref1' }, { name: 'ref1' }]
// which is actually [$objRef1, $objRef1, $objRef1] where $objRef1 is a pointer to the object address.

// when you updates the object,
obj.name = 'Hello'

// you still have array = [$objRef1, $objRef1, $objRef1] where $objRef1 all referes to this object: { name: 'Hello' }
// array === [{ name: 'Hello' }, { name: 'Hello' }, { name: 'Hello' }]

Solution:

You have to create a new object on every iteration, so they all are distinct object which a distinct address (pointer).

await axios
  .get("https://randomuser.me/api/")
  .then((r) => {
    const arr = r.data.results;
    arr.forEach((element) => {
       const item = {
          name: element.name.first,
          surname: element.name.last,
          gender: element.gender,
       };
       this.ar = item // refers to the new created item
       this.array.push(item);
     });
   }).catch((err) => {
      console.log("Error " + err);
   });
Kapcash
  • 6,377
  • 2
  • 18
  • 40
1

You can assign the response in an object inside the iteration and pass that in the this.ar. Try this way :

const URL = "https://randomuser.me/api/";
new Vue({
  el: '#app',
  data() {
    return {
      ar: {
        name: "",
        surname: "",
        gender: "",
      },
      userArray: [],
    }
  },
  methods: {
    post() {
      axios.get(URL).then((r) => {
        arr = r.data.results;
        arr.forEach((element) => {
          let obj = {};
          obj.name = element.name.first;
          obj.surname = element.name.last;
          obj.gender = element.gender;
          this.ar = obj;
          this.userArray.push(obj);
        });
      })
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.17.1/axios.js"></script>
<div id="app">
  <button @click="post()">POST</button>
  <h1>{{ar.name}} {{ar.surname}} {{ar.gender}}</h1>
  <br />
  <hr />
  <div v-for="(value, index) in userArray" :key="index">
    <span> {{index}} {{value}} </span>
  </div>
</div>
Debug Diva
  • 26,058
  • 13
  • 70
  • 123