Issue with updating bound property correctly when dynamically generating components using v-for in Vue.js

Encountered a challenge with vue.js and seeking guidance on the best approach to address it. Here's a concise summary of the issue:

Situation

Data is fetched from a rest API, handled by a class called DataLoader using javascript prototype syntax. The loaded data usually appears in repetitive formats like lists or cards. To simplify this process, a new component named DataLoaderWrapper was created, which takes a DataLoader object as a property. This wrapper displays loading spinners based on the loading state of the DataLoader, and includes a tag:

<slot :dataLoader='dataLoader' />

To access the data easily, the component can be used as follows:

<DataLoaderWrapper :dataLoader="myDataLoader">
  <template slot-scope="props">
    <template v-for="item in props.dataLoader.data">
       {{item}}
    </template>
  </template>
</DataLoaderWrapper>

Now, why do we use props.dataLoader instead of myDataLoader inside the template? This allows for direct access to dynamically generated DataLoader objects via component bound properties.


Expanding on this concept, consider having a DataLoader set up to load music releases:

<DataLoaderWrapper :dataLoader="myReleaseLoader">
...
</DataLoaderWrapper>

Each release is associated with one or more artists, requiring separate loading. An extension of the example would look something like:

<DataLoaderWrapper :dataLoader="myReleaseLoader">
  <template slot-scope="props">
    <template v-for="release in props.dataLoader.data">
      <h1>{{ release.Title }}</h1>
      Artists: 
      <DataLoaderWrapper :dataLoader="getArtistsLoader(release)">
        <template slot-scope="nestedProps">
          {{nestedProps.dataLoader.data.map(artist => artist.Name).join(', ')}}
        </template>
      </DataLoaderWrapper>
    </template>
  </template>
</DataLoaderWrapper>

The Problem

While releases load correctly and templates render as expected, new DataLoader instances are created dynamically to load artists for each release. However, the nestedProps variable fails to update once the DataLoader finishes loading the artist data.

After spending two days debugging, I'm certain that the data is successfully loaded but the spinner persists in the DataLoaderWrapper component. Checking nestedProps.dataLoader reveals data: [], which contradicts the component's displayed state. This issue might stem from Vue bindings being one-directional, where a child component update does not trigger a parent update.

A local instance with 'hot-reloading' updates UI changes without a full page reload when files are saved, resulting in different loader states during navigation. Since the cache implemented in the getArtistsLoader(..) function returns the same loader for a given release, the existing data reappears during UI updates. Attempts with computed properties, direct loader access, and adjusting property references failed to resolve the issue.

Any suggestions on how to tackle this challenge?

Answer №1

The issue with getArtistsLoader is that it does not return data synchronously

const data = getArtistsLoader(release);
// data === undefined

As a result, the DataLoaderWrapper receives undefined for the dataLoader props

Resolution

To address this, DataLoaderWrapper can now accept a promise as data, allowing it to update once the promise is resolved.

1. Update DataLoaderWrapper to handle promises

We can introduce an async prop to indicate whether the dataLoader function is asynchronous or synchronous.

<template>
  <div>
    <slot :dataLoader="realData"/>
  </div>
</template>

<script>
export default {
  props: {
    dataLoader: null,
    async: Boolean
  },
  data() {
    let realData = this.dataLoader;
    if (this.async) {
      realData = [];
      this.dataLoader
        .then(data => {
          this.realData = data;
        })
        .catch(error => {
          console.log(error);
        });
    }
    return {
      realData: realData
    };
  }
};
</script>

2. Modify getArtistsLoader to return a promise

function getArtistsLoader(release) {
  return axios.get('/artists?release=' + release.id)
    .then((response) => response.data)
}

3. Include the async prop

<DataLoaderWrapper async :dataLoader="getArtistsLoader(release)">

Full demo available at

https://codesandbox.io/s/2053k38k6r?module=%2Fsrc%2FApp.vue

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

Finding elements based on their position using Javascript

I'm searching for an optimal method to identify all elements located within a specific range of pixels from the top of the page. Currently, I have implemented a straightforward test called inRange function inRange(el, from, to) { var top = el.offs ...

Node.js: What is the best way to manage callbacks within a loop structure?

My current attempt at implementing functionality using Node.js and the Box SDK is unfortunately failing. Here's a snippet of my code: var connection = box.getConnection(req.user.login); connection.ready(function () { connection.getFolderItems(0, nu ...

Switch between selection modes in React JS DataGrid using Material UI with the click of a button

I've been working on creating a datagrid that includes a switch button to toggle between simple and multiple selection modes. const dispatch = useDispatch(); const { selectedTransaction } = useSelector(...) const [enableMultipleSelection, setEnableMu ...

Is there a way to verify if the JSON Object array includes the specified value in an array?

I am working with JSON data that contains categories and an array of main categories. categories = [ {catValue:1, catName: 'Arts, crafts, and collectibles'}, {catValue:2, catName: 'Baby'}, {catValue:3, catName: 'Beauty ...

Pass information from Vue JS v-for to a button when it is clicked

Just started learning Vue JS and encountered a small issue I'm currently looping through an array with a button inside the div I'm iterating over The goal is to grab the data of the selected item after clicking on the button For example, suppo ...

Can the Route be modified in Next.js?

I have developed search functionality that navigates to "/search/xxxx", but it is located in the header. Every time I perform a search, the route gets repeated and becomes "/search/search/xxx". Could you suggest any way other than usin ...

The number input form does not recognize keyboard input as expected

<template v-for="(paint, index) in paints"> <input type="number" v-bind:min="1" v-model.number="paint.qty"> </template> - var paintListApp = new Vue({ delimiters: ['${', '}'], el: '#paintListApp', ...

Incorporate seamless integration by using the npm install command

I am currently facing an issue where I need to identify and remove unused dependencies from my package.json file every time I run npm install for my app. Is there a method to automatically include the npm package https://www.npmjs.com/package during the n ...

What causes a Next.js App to crash when a prop is not defined in destructuring code?

Let me share the issue I am facing. I have developed a custom Context API wrapper to handle all my data. However, there is this docType property that may not always be defined or may not exist at times. When I destructure it in this way: const { docType } ...

I am seeking assistance with my code. It is passing most of the test cases, but is failing only two without any error messages. What could

I recently started teaching myself programming, so please excuse me if my question seems a bit basic. One of the challenges on CodeCamp requires defining a function that takes an array with 2 values as input and returns the Least Common Multiple (LCM) of ...

Converting a PHP string into a JSON array

Currently, I am working on making a cURL request and receiving a response that is in the following format when using var_dump: string(595) "{"user_id":1,"currency":"eur","purchase_packs":{"1":{"amount":500,"allowed_payment_methods":["ideal","paypal","visa ...

What is the most effective way to refine the ${{ data }} object to display only particular values?

Utilizing vue.js, I am retrieving data ${{ data }} and presenting it to the user. However, I only want to showcase specific values. In this case, I wish to display everything except for Actions. The information to be displayed includes: Name, Description, ...

Ways to send information from Vue instance to nested components

Currently, I am fetching data using AJAX from the Vue instance and trying to pass it onto different components. As I delve deeper into learning Vue.js, I can't help but notice that the documentation seems a bit scattered... This snippet showcases wha ...

How do I reduce the size of a WinJS image file

Can anyone help me figure out how to retrieve the size (in pixels, bytes) of a picture in a Windows 8 app? I'm using openPicker to select the file but can't seem to find the size attributes. I want to display an error message if the file is too ...

Performing bulk operations on multiple documents in MongoDB by specifying a custom identifier for updating or

Recently, I've been working with a mongo schema const taskSchema=new Schema({ userID:{type:ObjectId,required:true}, task: { type: String, required: true, trim: true, maxlength: 30, }, finalDate:{type:Date,required:true}, ...

Adding elements to a two-dimensional array using AngularJS

How can I assign the value of an input to tasks.name and automatically set status: false when adding a new item to the $scope.tasks array? HTML <input type="text" ng-model="typeTask"> <button ng-click="updateTasks()">Add task</button> ...

How can I retrieve JSON data from an AJAX request on an HTML page?

How can I display my JSON data on an HTML page using this JavaScript code? $.ajax({ url : 'auth.json', type: "GET", dataType : "jsonp", success: function(result) { $.each(result, function(i, v) { // Loop through each record in ...

Mastering the Art of Unit Testing JavaScript Asynchronous Code with Jasmine Mocking

Hello, I recently started learning JS and encountered a problem while testing asynchronous methods with jasmine mocking. Below is the code snippet I am trying to test: test.factory('service', ['$http', '$q', function($http, ...

Discover a specific segment of the pie chart

Currently, I have a pie chart that has been created using HTML5 canvas. I am able to retrieve the coordinates (X,Y) when the mouse hovers over it. Now, my goal is to determine which slice of the pie chart the Point (X,Y) falls into. Please note: I have ...

"Knex can't keep up with the blazing speed of NodeJS iteration

Encountering challenges with using a for-loop containing queries (knexjs.org)? Let's explore how I traverse through my array. The structure of my array is as follows: [ { module_id: 9, able: '1', will: '1' }, { module_id: 9, able: ...