18

I'm stuck since a few weeks in a problem with rendering data with VueJS.

What I'm doing is making some axios calls (one inside another). My problem is that the data is rendered before the calls has completed, so the view is not showing anything.

I saw some codes that do some "await" and "Async calls" but nothing seems to solve my problem.

Also there is something similar here Get component to wait for asynchronous data before rendering But isn't working either

here is my code:

    <template>
     <div class="m-portlet m-portlet--full-height" m-portlet="true" id="m_portlet_validate_agenda">
    ...
    <div class="m-portlet__body">
        <div class="tab-content">
            <div class="tab-pane active" id="m_widget2_tab1_diagnose">
                <div class="m-widget2">
                    <div v-for="diagnose in diagnoses" v-if="diagnoses.length" :class="'m-widget2__item m-widget2__item--' + diagnose.delayColor[0]">
                        <div class="m-widget2__checkbox" >
                            <label class="m-checkbox m-checkbox--solid m-checkbox--single m-checkbox--brand">
                                <span class="m--bg-white" v-html="diagnose.concurrence"></span>
                            </label>
                        </div>
                        <div class="m-widget2__agenda col-2">
                            {{ diagnose.started_at | moment("HH:mm A") }}
                        </div>
                        <div class="m-widget2__desc" v-if="!isFetching">
                            <div>
                                <span class="m-widget2__text">

                                </span><br>
                                <span class="m-widget2__user-name">
                                    <a href="#" class="m-widget2__link m-link">
                                    Paciente: 
                                    {{ diagnose.details[0].name }}
                                    </a><br>
                                    <a href="#" class="m-widget2__link m-link">
                                    Tratante: 

                                    </a>
                                </span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
         </div>
       </div>
     </div>
    </template>

    <script>
    export default {

        data() {
            return {
                events: [],
                diagnoses: [],
                urgencies: [],
                treatments: [],
                isFetching: true
            }
        },

        mounted() {
            this.loadData();
        },

        methods: {

            loadData: async function() {
                await axios.get('/pacientes/request-json/agenda/validarAsistencia/eventos').then(res => {
                    this.events = res.data;
                    this.diagnoses = [];
                    this.urgencies = [];
                    this.treatments = [];
                    this.getDetails();
                    this.getDelayColor();
                    this.getConcurrence();
                    vm.$nextTick(function () {
                        $('[data-toggle="m-tooltip"]').tooltip();
                    });
                    console.log('end LoadData');
                });
            },

            getDetails: function() {
                console.log('cargando');
                this.events.forEach(event => {
                    axios.get('/pacientes/request-json/agenda/validarAsistencia/eventos/' + event.id).then(res => {
                        event.details = res.data;
                        console.log(res.data);
                    });
                });
                this.distributeEvents();
                console.log('montado');
            },    

            distributeEvents: function() {
                this.events.forEach(event => {
                    if ( event.event.event_type == "diagnosis" )
                    {
                        this.diagnoses.push(event);
                    }
                    else if ( event.event.event_type == "urgency" )
                    {
                        this.urgencies.push(event);
                    }    
                    else if ( event.event.event_type == "treatment" )
                    {
                        this.treatments.push(event);
                    }
                });
                this.isFetching = false;
            },

            getDelayColor: function() {
                this.events.forEach(event => {
                    do something...
                });
            },

            getConcurrence: function() {
                this.events.forEach(event => {                    
                    do something...
                });
            },

            diffMinutes: function(started_at) {
                do something...
            }

        }

    }

3 Answers 3

33

To prevent the component from rendering before the data have returned you could:

  1. add a "isFetching" property to the data and set it to "true".
  2. in the fetch callback, set isFetching to "false".
  3. add v-if="!isFetching" to the wrapper of the component.
Sign up to request clarification or add additional context in comments.

4 Comments

Should I need to implement a watcher also ??
no, The "isFetching" property is in the 'data' object, meaning that vue watching it for us, and will update the component automatically once changed. no watch needed.
Doesn't seem to work either... [Vue warn]: Error in render: "TypeError: diagnose.details is undefined"
Move the 'v-if="!isFetching"' to the wrapper, the first div in the component. Or at least to to wrap the access to the variable in the template
4

You aren't handling Promises properly, so they keep getting unresolved. You can use async, await, although I prefer myself using plain Promise Objects:

getDetails() is another story. You are making a loop, and forEach loop you are sending an axios request. You could have to store each Promise returned by each axios call in an array, to call then Promise.all.

    getDetails: function() {
        let url = '/pacientes/request-json/agenda/validarAsistencia/eventos/';
        console.log('loading');
        let promisedEvents = [];

        this.events.forEach(event => {
            promisedEvents.push(axios.get(url + event.id))
        });

        return Promise.all(promisedEvents)
    },

After that I would do something like this:

    loadData: function() {
        axios.get('/pacientes/request-json/agenda/validarAsistencia/eventos')
            .then(res => {
                this.events = res.data;
                this.getDelayColor() // sync operation; no need to be returned
                return this.getDetails(); // Return the promise(s)
            })
            .then((res) => {
                // do something with the response from your array of Promises
            })
            .then(anotherPromise) // You can also return a promise like this 
            .catch(handleError) // Very important to handle your error!!
        });
    },

I am not saying this is the best way to achieve what you may want, but that it is one way to make your code to work. Important here is that you need to learn about Promises.

5 Comments

Thanks I will try that, I also will read something about promises
Let me know if you need help to clarify something!
Also, hablo español. :)
there are a bunch of documentation that discuss about to prefer async/await instead of promises
@Pablo Araya Sure, but you should understand promises before jumping in async/await. In the end, I like personally more the syntax of Promises more.
1

The Solution:

Thanks you guys !

         loadData: function() {
            axios.get('/pacientes/request-json/agenda/validarAsistencia/eventos')
                .then(res => {
                    this.events = res.data;
                    this.getDelayColor() // sync operation; no need to be returned
                    this.getConcurrence();
                    vm.$nextTick(function () {
                        $('[data-toggle="m-tooltip"]').tooltip();
                    });
                    return this.getDetails(); // Return the promise(s)
                })
                .then((res) => {
                    console.log(res.length);
                    for (var i = 0; i < res.length; i++) {
                        this.events[i].details = res[i].data;
                    }
                    this.distributeEvents();
                    console.log('end LoadData');
                })
                .catch(error => {
                    console.log('error');
                })
        },

        getDetails: function() {
            let url = '/pacientes/request-json/agenda/validarAsistencia/eventos/';
            let promisedEvents = [];

            this.events.forEach(event => {
                promisedEvents.push(axios.get(url + event.id))
            });

            return Promise.all(promisedEvents)
        },

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.