1

I have a strange problem in my Vue application. The component looks like this:

...
<template v-for="foo in foos">
   <Elm v-if="foo.visible" :key="foo.label" :bar="foo" />
</template>
...

"Elm" is a value in an object, retrieved from a JSON file. The component is reactive if I get the JSON file locally:

<script>
    import datas from "datafile.json";
    ...
    methods: {
        fillFoos() {
            datas.forEach(data => {
               this.foos.push(data)
            })
        }
    },
    mounted: {
        this.fillFoos()
   }
   ...
</script>

But when I retrieve the file remotely using fetch, the component is no longer reactive and no longer disappears when the foo.visible value is changed :

<script>
    methods: {
        getDataFromApi() {
            return new Promise((resolve, reject) => {
                fetch(this.apiUrl)
                .then(response => {
                    return response.json();
                })
                .then(jsonResponse => {
                    resolve(jsonResponse);
                })
                .catch(e => {
                    ...
                })
            })
        },
        fillFoos() {
            this.getDataFromApi()
            .then(response => {
                response.forEach(data => {
                    this.foos.push(data);
                });
            });
        }
    },
    mounted: {
        this.fillFoos()
    }
    ...
</script>

In both cases the "foos" array is correctly filled, the only difference is that the v-if directive seems to be broken in the second case. To be more precise, the display is done correctly at initialization (foo.visible is true for all the elements and they're all displayed), but in case of a later change of the foo.visible value, they don't disappear.

I can't find what's wrong...

Omid Nikrah
  • 2,444
  • 3
  • 15
  • 30
Joulss
  • 1,040
  • 1
  • 14
  • 26
  • In your template tag, where you are using the `v-for` is that the template tag for a `.vue` file? Also, can you share the payload that you get when you resolve the promise? – Daniel Ormeño Oct 25 '18 at 21:59
  • The template tag is not the root element of the .vue file (if that's what you meant) – Joulss Oct 25 '18 at 22:01
  • You've implemented the [explicit promise construction antipattern](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it), don't do that – Phil Oct 25 '18 at 22:40

2 Answers2

1

I believe the issue you are having is that the method getDataFromApi is returning a promise, but when you consume it in fillFoos the promise is not awaited, instead you call forEach on it.

You need to use the getDataFromApi().then(x => {}) syntax to resolve the promise, or alteratively you can use async await.

You can try something like this

methods: {
    async getDataFromApi() {
        const response= await fetch(this.apiUrl);
        return response.json();
    },
    async fillFoos() {
        try {
            await foos = this.getDataFromApi();
            this.foos = foos;
        } catch(error) {
            //handle error.
        }
    }
}
Daniel Ormeño
  • 2,743
  • 2
  • 25
  • 30
0

Someone posted a response very close to the solution yesterday but deleted it, I don't know why. The problem was that I stored the fetch response in a variable in the data section, before using it to fill in the "foos" table

data: function() {
    return {
        dataFromApi: null
    }
}

By removing this variable, and thus creating it on the fly after the fetch, everything works normally.... I didn't specify that I stored the answer in this variable because I didn't think it could be related... Morality: always specify everything !

Joulss
  • 1,040
  • 1
  • 14
  • 26