How can I ensure that VueJS only starts loading data after the initial API call has been completed?

After retrieving data from an API, I populate a form in my component. The challenge I am facing is that the watchers are triggered immediately after populating the initial data. I want them to be triggered asynchronously. Additionally, I need to prevent the Update button from being enabled if any value has changed after the initial data population.

<template>
  <div id="app">
    <input type="text" v-model="user.userId" /> <br />
    <br />
    <input type="text" v-model="user.title" /> <br />
    <br />
    <button :disabled="isDisabled">Update</button>
  </div>
</template>

<script>
export default {
  name: "App",
  watch: {
    user: {
      handler(oldVal, newVal) {
        if (oldVal != newVal) {
          this.isLoaded = false;
        }
      },
      deep: true,
    },
  },
  computed: {
    isDisabled() {
      return this.isLoaded;
    },
  },
  async created() {
    await fetch("https://jsonplaceholder.typicode.com/todos/1")
      .then((response) => response.json())
      .then((json) => {
        this.user = json;
        this.isLoaded = true;
      });
  },
  data() {
    return {
      user: {
        userId: 0,
        id: 0,
        title: "",
        completed: false,
      },
      isLoaded: true,
    };
  },
};
</script>

I have looked into resources like Vue, await for Watch, Are watches asynchronous?, and Vue.js How to watcher before mounted(), can't get data from watch, but I am struggling to implement their suggestions.

See the live example here: https://codesandbox.io/embed/great-euler-skd3v?fontsize=14&hidenavigation=1&theme=dark

Answer №1

In order to arrive at a solution, certain conditions must be taken into consideration.

Although isLoaded currently determines the state of initial loading, its name can cause confusion as it actually signifies that data is not loaded.

A more appropriate approach could be:

  watch: {
    user: {
      if (this.isLoading && oldVal != newVal) {
        this.isLoading = false;
      }
      ...

The watcher may not require being deep and can be removed when no longer necessary:

async created() {
  let unwatchUser = this.$watch('user', (oldVal, newVal) => {
    if (this.isLoading && oldVal != newVal) {
      this.isLoading = false;
      unwatchUser();
    }
  })
  ...

An alternative way to indicate that data has not been loaded yet is by setting it to null, representing no value. This eliminates the need for an isLoading flag or a watcher. If null is unsuitable due to referred object properties, optional chaining and conditional rendering can be utilized:

  <div v-if="user">
      <input type="text" v-model="user.userId" />
      ...
  <div v-else class="spinner"/>

Answer №2

Here is a straightforward solution to the problem:

Q: How can I wait for the initial API data load in VueJS before displaying?

A: You can add a flag inside your watch (e.g. isLoaded).

Additionally, there are a few issues with your code:

  • Using async/await in created does not serve any purpose,
  • The isDisabled variable is unnecessary since it depends on only one value from data. You can directly use this value (isLoading) instead.
  • If your API calls fail, the isLoading flag will not change. A better approach would be to move it to the `finally` block.

For the solution to your specific issue, you can refer to this codesandbox:

<template>
  <div id="app">
    <div v-if="!isFetching">
      <input type="text" v-model="user.userId" /> <br />
      <br />
      <input type="text" v-model="user.title" /> <br />
      <br />
      <button :disabled="!isLoaded">Update</button>
    </div>
    <div v-else>Loading...</div>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      user: {
        userId: 0,
        id: 0,
        title: "",
        completed: false,
      },
      isFetching: false,
      isLoaded: false
    };
  },
  watch: {
    user: {
      handler(oldVal, newVal) {
        if (!this.isFetching) {
          // The comparision here doesn't work as oldVal/newVal are objects
          if (oldVal != newVal) {
            this.isLoaded = false;
          }
        }
      },
      deep: true
    },
  },
  created() {
    this.isFetching = true;
    fetch("https://jsonplaceholder.typicode.com/todos/1")
      .then((response) => response.json())
      .then((json) => {
        this.user = json;
        this.isLoaded = true;
      })
      .finally(() => this.isFetching = false)
  },
};
</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to eliminate a trailing slash from a URL?

Currently, I am utilizing Nuxt.js along with nuxt-i18n in the mode of "strategy: 'prefix'". If you want to learn more about this strategy, check out this link. However, I am facing an issue: whenever I try to access the homepage of my application ...

Tips for resolving the issue: SyntaxError - Unexpected token import

I've encountered this error in the past and have looked at other solutions, but none of them seem to work for my specific issue. My package.json file includes the following dependencies: "dependencies": { "axios": "^0.15.2", "babel": "^6.5. ...

How does SWR affect React state changes and component re-rendering?

I am currently utilizing SWR for data fetching as outlined in the documentation: function App () { const [pageIndex, setPageIndex] = useState(0); // The API URL incorporates the page index, which is a React state. const { data } = useSWR(`/api/data? ...

JSfiddle not loading properly on website

I am facing an issue with my Jsfiddle code. It works correctly there, but when I copy it into my webpage along with the CSS and JavaScript files, it doesn't work. Can anyone provide insight on how to properly transfer the code to display it correctly ...

Ways to troubleshoot setTimeout functioning during HTTP requests?

Utilizing Socket IO in my client app, I have implemented a feature to check for unread events. The process involves making a request to the backend, which then sets a 5-second timeout before checking for any new events and sending them back. // client soc ...

Traversing an array of objects using D3.js

I'm attempting to create a row of bars that are all the same height and width based on their titles. I have an array of 100 objects, each with a key called results containing another array of 20 objects. This means I should end up with a row of 2000 b ...

I am trying to update the content in ckeditor when a different option is selected, but for some reason it is not working

jquery issue: the jquery that I added and the content doesn't change even though the "alert(model);" works fine <script> $(document).ready(function() { $("select.modele").change(function() { var modele = $(this) ...

Creating multiple objects using a single object in JavaScript can be achieved by using the concept of object

When presented with the following data structure: { "time_1": 20, "time_2": 10, "time_3": 40, "time_4": 30 } and expecting a result in this format: [ { "key1": "time_1" ...

How can I prevent pre-defined array items from being replaced by database values?

Incorporating Vue Select into my project to create a tag list for users to add and save to a database has been successful. However, I'm facing an issue where I want to include some predefined tags in the list for users to select from. When attempting ...

Exploring the effectiveness of testing Svelte components

Looking to test a component that utilizes a third-party module without mocking the imported components? Check out this example: // test.spec.ts import Component from "Component"; describe('Component', () => { test('shoul ...

Highlighting with pretty JSON formatting

Is there a way to format JSON on a website and emphasize certain text or lines within it? Ideally, I'm looking for an IFRAME service that I can link to a URL where the JSON is downloaded and displayed as HTML. I want to be able to specify a search st ...

Concealing a div based on empty textboxes using backbone and javascript

I need assistance with displaying or hiding a div based on the length of text boxes. My project is structured using Backbone, so I am unsure about where to insert the code.. Here is my current code; <input type="text" id="txtUsername" placeholder="use ...

Having difficulty modifying the values of my input types on the edit page

After successfully adding user values from the add user page, I encounter an issue when attempting to edit these values such as firstname and lastname on the edit page. Please review the code snippet below for any possible errors. import React from ' ...

Error event triggered by Ajax call despite receiving 200 ok response

$.ajax({ url: 'http://intern-dev01:50231/api/language', type: 'GET', dataType: 'json', success: function() { console.log('Success! The call is functioning.'); }, ...

Maintain vertical spacing within Vue templates using Prettier

I'm currently utilizing the eslint-plugin-vue in my Vue project. In my project, I have a specific configuration defined in the .prettierrc file: // /.prettierrc { "arrowParens": "avoid", "bracketSpacing": true, "insertPragma": false, "jsxBra ...

The axios library fails to send formdata to the server

Recently, I've been working on an axios code to handle form data submission. Here's the snippet of the code: updatesetting: function(){ let formData = new FormData(); formData.append('email', this.email); formData.append(&a ...

Customize and Enhance Code for Website Integration

My code is fetching a script from an external website. var url = "//example.com/script-url.js"; $.ajax({ url: url, dataType: 'jsonp' }); Although it functions properly, the script retrieved is created by a different website. I need to make ...

How to trigger a hover effect on a div and its child simultaneously using HTML and jQuery

Here's my concept: I want the text to be contained within div elements with an integrated image, rather than just having fading in and out pictures. Here's my attempt: #first{ position: absolute; } #second{ position: absolute; -we ...

Tips for repeatedly clicking a button over 50 times using Protractor

Is it possible to click the same button more than 50 times using a loop statement in Protractor? And will Protractor allow this action? Below is my locator : var nudge= element(by.xpath("//a[@class='isd-flat-icons fi-down']")); nudge.click(); ...

Ensuring that md-select(s) created through ng-repeat are linked to the same model

<div ng-repeat="(key, value) in dataSet | groupBy: 'partner.partnerName'"> <md-select ng-model="userName" placeholder="{{ key }}" class="partnerUser" > <md-option >{{ key }} </md-option> <md-option ng-repe ...