5

I am implementing Vue.js dynamic components but I can't seem to figure out how to show the component only when it has fetched it's data. Until then it should show the old component.

The scenario is this. When the page is loaded it should show the Home component. Then I click on "Show posts". Nothing should happen until the Posts component has fetched it's posts. Then it should show the Posts component. I don't want any loading to show.

I could fetch posts in my Home compoent but I think the Posts component should be responsible for that. Also if I have many components I don't want to fetch all their data in my Home component. They should fetch their own data (I think). Is this possible to do?

home.js

import Home from './home.js'
import Posts from './posts.js'

export default {
    template: `
        <div>
            <a @click="showPosts">Show posts</a>
            <component :is="currentComponent" />
        </div>
    `,

    methods:
    {
        showPosts()
        {
            // Do this ONLY after the Posts component has fetched it's data... not immediately...
            this.currentComponent = Posts
        }
    },

    data:
    {
        currentComponent: Home
    },
}

posts.js

export default {
    template: `
        <div>
            <div v-for="post in posts">{{ post.body }}</div>
        </div>
    `,

    data:
    {
        posts: [],
    },

    created()
    {
        axios.get('/posts').then(({ data } => {
            this.posts = data
        })
    },
}

2 Answers 2

2

You can use the async created hook in combination with a v-if statement.

So you basically wrap your template in a v-if

template: '<div v-if="dataloaded">This is component xlr</div>

Inside the async created you load your data and set the flag to true once it is finished

  async created() {
  await loadData(3000);
  this.dataloaded = true;},

See a working fiddle here: https://jsfiddle.net/Postlagerkarte/yomb5qv6/

Sign up to request clarification or add additional context in comments.

3 Comments

The problem with this is that first component disappears and then after a while the second appears. The first component shouldn't disappear until the second appears.
I have been looking for an example that works for a while now. Your working jsfiddle was so so helpful. I can't thank you enough! Plus your async sleep was so perfect and simple.
This might work depending on your use case but it's worth pointing out using async for lifecycle hooks is not officially supported and may lead to unexpected behavior.
1

If you want to show posts component only when posts are fetched, the only way is to fetch data in the parent component. To do this you normally decouple the fetching from the component. You have something like a apiService, which fetches posts and the Posts component really is only the view and its only purpose is displaying data. This way your code also becomes much more re-usable, since many api requests probably share a lot of logic.

Here is how your component could look like:

home.js

import Home from './home.js'
import Posts from './posts.js'
import apiService from '../services/apiService.js'

export default {
    template: `
        <div>
            <a @click="showPosts">Show posts</a>
            <component :posts="posts" :is="currentComponent" />
        </div>
    `,

    methods:
    {
        showPosts()
        {
            apiService.fetchPosts().then((response) => {
                this.posts = response.data.posts
                this.currentComponent = Posts
            });
        }
    },

    data:
    {
        posts: []
        currentComponent: Home
    },
}

5 Comments

Thanks, I was starting to think the same thing. It looks like the only possibility is to fetch all data in the parent component. It would be better if each component could fetch it's own data.. but I guess this will have to do for now...
it is possible, but the component has to be initialised before and therefor it will render before. This is why people add loading overlays to components. It is also more user-friendly, since the user immedietly gets feedback after he clicks the button. Your approach means, that if the user clicks the button and the requests takes 3 seconds, your page will look and feel really unresponsive, because nothing is happening for 3 seconds. Normally the user will smash this button and send out many more requests.
that is true, normally I would show a loading. But this an app for TV (like a slideshow) so I didn't want to display any loading. So a very specific use case I guess.
if you have a slideshow you should still show a loading animation initially and then lazy load more images as soon as the user gets to the end of loaded data. but yeah that's implementation details. consider accepting my answer. greetings.
Thanks @oshell I ended up with an apiService

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.