Home > Net >  Bind a JavaScript array to a Vue 3 component's v-for in template to update DOM
Bind a JavaScript array to a Vue 3 component's v-for in template to update DOM

Time:02-13

I've just started learning Vue 3 and I'm trying to create a Vue Component that is bound to a JavaScript array (defined and modified outside Vue) and uses that array to render a v-for list of child Vue components.

In the code below, I have my "layer-container" Vue component that has a template that renders a list of "layer-item" subcomponents using data from a JavaScript array layersGlobal. If you run it, you can see that the items are rendered properly from the initial data in the array, but when the array is updated (after 1 second, via a push() call in setInterval()) the list is not rerendered, and the DOM is not updated.

<html>

<body>
  <div id="layers">
    <layer-container />
  </div>

  <script src="https://unpkg.com/vue@3"></script>
  <script>
    var layersGlobal = [
      {name: "Layer A"},
      {name: "Layer B"}
    ];

    let app = Vue.createApp({
      data: function() {
        return {}
      },
      methods: {}
    });

    app.component("layer-container", {
      template: `
                  <div >
                      <layer-item v-for="layer in layers" v-bind:name="layer.name" v-bind:key="layer.name"></layer-item>
                  </div>
              `,
      components: ["layer-item"],
      data: function() {
        return {
          layers: layersGlobal
        }
      },
    });

    app.component("layer-item", {
      template: `
                  <div >
                      <span>{{ name }}</span>
                  </div>
              `,
      props: ["name"]
    });

    app.mount("#layers");

    setTimeout(() => {
      layersGlobal.push({name: "Layer C"});
      console.log("Layers:", layersGlobal);
    }, 1000);
  </script>
</body>

</html>

Am I doing something wrong, or misinterpreting how Vue is reactive with arrays? Is it possible to bind a JavaScript array declared outside Vue to a Vue component like this, or can/should I be doing it another way?

I've looked around for a solution and found some example code that works in Vue 2, but not Vue 3. Any help would be much appreciated.

Thanks.

CodePudding user response:

Reactive data must be updated within the context of the component. You probably want to call setTimeout inside of the component's definition and update the reactive variable this.layers instead of the global array.

app.component("layer-container", {
    /* ... */
    data: function () {
        return {
            layers: layersGlobal
        }
    },
    mounted() {
        setTimeout(() => this.layers.push({ name: "Layer C" }), 1000)
    }
})

Alternatively, if you need to update the data from outside the component, you need to explicitly define your global array as a reactive object, using the ref() function.

const layersGlobal = Vue.ref([ /* ... */ ])

setTimeout(() => layersGlobal.value.push({ name: "Layer C" }), 1000)

Note the .value property being used to access the actual object.

  • Related