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

What is the best way to adjust the width of a navbar using JavaScript?

I am experimenting with a responsive design and facing an issue - the width of my #menu in CSS is initially set to 0, but I need to change this value based on user input using JavaScript. Now, I want to dynamically adjust the width of the menu for differe ...

When a user clicks on a React listItem, the information for that specific item is displayed using

As a beginner in the coding world, I am currently learning about React and JSON. My project involves working on three interconnected panels. Specifically, I aim to showcase checklist answers on the third panel. First Panel: Displaying: All the ESN ("46 ...

React Switch not displaying various pages correctly

After creating a new component to switch between pages on my React app, I encountered an issue where the HomePage renders correctly when entering the site, but clicking on navlinks does not work. Additionally, when trying to access the url /contacto, ins ...

Are you familiar with Mozilla's guide on combining strings using a delimiter in Angular2+?

I found myself in need of concatenating multiple string arguments with a specific delimiter, so after searching online, I stumbled upon a helpful guide on Mozilla's website that taught me how to achieve this using the arguments object. function myCo ...

Styling CSS for disabled nested elements

In a project I am currently working on, I've noticed that disabled items do not appear disabled enough. My initial plan was to easily address this issue with some CSS. Typically, adjusting the opacity is my go-to solution to achieve the desired effec ...

Encountering an issue with displaying data fetched from omdbapi, receiving [object Object] instead

Everything is working perfectly in my code except for the mysterious appearance of [object Object] whenever I perform a search. I've combed through my code multiple times, but I just can't seem to locate the source of this issue. It would be grea ...

Creating a dynamic star rating system using CSS

A unique code pen project showcasing a pure css star rating interface can be found at: https://codepen.io/yaworek/pen/JJpEaZ Below is the custom css part: /*** * Custom Pure CSS Star Rating Widget for Bootstrap 4 * * www.TheMastercut.co * ***/ ...

Request with an HTTP header for authentication

Here's a straightforward question that may seem simple to some developers but could be tricky for beginners. So, my question is - how can I send an HTTP request from a browser when a user clicks a button and also add an authorization header to the re ...

Adding identifiers to columns in DataTables depending on the value of another attribute

I am facing a challenge with inserting the <span> tag into the "salesStatusName" column cell that already contains some data. This should only happen when the value of the last column, "completelyDelivered", is true. However, I do not want to display ...

I am attempting to draw a correlation between two numerical variables

I'm struggling to compare two scores in my game. When they match, I want it to display "You won", but I can't get it to work. I've attempted using the parseInt method and .val method, but no luck so far. var numberFour = Math.floor(Math.ra ...

Creating a Dynamic Dropdown Menu in Rails 4

I am attempting to create a dynamic selection menu following this tutorial; however, I am encountering issues as the select statement does not seem to be updating. Below is the code snippet I currently have: #characters_controller.rb def new ...

Utilizing a Dependency Injection container effectively

I am venturing into the world of creating a Node.js backend for the first time after previously working with ASP.NET Core. I am interested in utilizing a DI Container and incorporating controllers into my project. In ASP.NET Core, a new instance of the c ...

Issue with Flask-Cors in Nuxt with Flask and JWT authentication implementation

I have exhausted all the available solutions to my issue, but I still can't seem to pinpoint the problem. Despite trying every solution out there, nothing seems to be of any help. Every time I make a request, the browser blocks it due to CORS Policy ...

Using jQuery to include Chinese characters in a request header

When making a jQuery post request, I need to set client information in the header. This is how my call looks: jQuery.ajax({ url : myURL, type : "POST", beforeSend : function(request){ request.setRequestHeader('someInfo', clie ...

Struggling to locate the element for clicking or sending keys in Selenium using Java?

Hi everyone, I'm a new member of this forum and I'm just starting out with coding for the first time. I have encountered an issue with a specific part of my code: md-input-container class="md-default-theme md-input-invalid">> label for="i ...

Sorting through a list post a retrieval action

Could you guys please help me understand why my code is not functioning properly? I am receiving an array from my backend rails API, which is providing the data correctly. I have created an empty array where I filter the records based on their ID. The fi ...

Is there a way to retrieve all IDs within an array of objects using a foreach loop and merge the data associated with each ID?

I am currently working on a TypeScript API where I have merged all the data from this API with some additional data obtained from another API function. I have provided a snippet of my code which adds data to the first index of an array. M ...

`In Vue.js, it is not possible to access a data property within a

I encountered an issue while attempting to utilize a property of my data within a computed method in the following manner: data() { return { ToDoItems: [ { id: uniqueId("todo-"), label: "Learn Vue", done: false }, ...

Can flexbox elements be animated as they scroll?

Wondering if it's feasible to animate flex elements upwards when scrolled? Attempting to replicate this effect: https://codepen.io/Sergiop79/pen/bxjGEe I want to apply this to the elements below (styled in flexbox), either the entire "row" or each i ...

Retrieve JSON key values dynamically with jQuery

My JSON data is dynamic, and I need to access the 'bf' key within it. The keys 'xxxxxx20160929' and 'yyy813AI20160929' can change but the structure of the JSON remains consistent. { "resultData": [ { "a": "124", ...