Cycle through images that are dynamically generated from retrieved data

Although I have a functional solution for this issue, it feels messy and not the ideal way to handle it in Vue.

The challenge is fetching data from a backend related to a "Vendor" entity, which includes photos that need to be displayed on the page. The goal is to show one photo at a time and then rotate through them every 5 seconds by changing their opacity using setInterval.

I have a 'ref' for the images but cannot access it within '.then' because this.$refs is not available in created(). Furthermore, the "refs" are also unavailable in mounted() due to the asynchronous fetch call in created().

Placing setInterval in update is not an option since it would create a new listener for each update attempt (yes, I tried that...).

My current approach involves updated() setting this.photoCount whenever there's an update. setInterval is implemented in created() but remains inactive until this.photoCount is no longer null.

<template>
    <!-- Your template content here -->
</template>

<script>
export default {
    data(){
        return {
            vendor: {},
            banner: {
                displayed: false,
                type: "",
                message: ""
            },
            displayedPhoto: 0,
            photoCount: null
        }
    },

    created(){
        let token = localStorage.getItem("jwt");
        let headers = {"Content-Type": "application/json"};
        if(token !== null) headers["Authorization"] = `Bearer ${token}`;

        // Fetch vendor data
        fetch(`http://localhost:8000/vendor${document.location.pathname}`, {
            method: "get",
            headers: headers,
        })
            .then(r=>r.json())
            .then((vendor)=>{
                this.vendor = vendor;
            })
            .catch((err)=>{
                console.error(err);
            });

        // Setting up photo rotation with setInterval
        setInterval(()=>{
            if(this.photoCount !== null){
                if(this.displayedPhoto >= this.photoCount){
                    this.displayedPhoto = 0;
                }else{
                    this.displayedPhoto++;
                }
            }
        }, 5000);
    },

    updated(){
        // Calculate photo count once refs are available
        this.photoCount = this.$refs.vendorPhoto.length;
    }

What could be a more efficient and "vue-like" approach to tackle this issue? While my current solution works, it seems suboptimal.

Answer №1

What I would do differently:

  • Avoid counting $refs. The necessary information is already present in vendor.photos.length
  • photoCount should be a computed property. Keeping it as a separate state variable risks it falling out of sync with vendor.photos.count, which could result in subtle bugs. It is best practice for derived state (e.g: computed properties, store getters) to always remain derived and not duplicate information in multiple places within the state.
  • The function that increments the displayedPhoto variable could be simplified to:
methods: {
  changePhoto() {
    this.displayedPhoto = (this.displayedPhoto + 1) % this.photoCount
  }
}
  • Fetching vendor data should be handled by a standalone method (e.g: fetchVendor). This allows for re-fetching when necessary based on business logic, without being tied to the component lifecycle.
  • I have moved the fetch operation to the mounted hook. The created hook should only contain code that needs to run before the component is added to the DOM, fetching data does not fall into that category.
    Fetching data in created can give the false impression that the component may render before the data is fetched, even if the backend is running locally. It is better to fetch data in the mounted hook and handle a suitable "loading..." state (e.g: loading indicator, placeholder image, etc...)
  • I want to highlight:
    { hidden: i !== displayedPhoto % photoCount }
    . I included the % photoCount part to cover edge cases where switching from a vendor with, for example, 10 images to one with 5 images might cause no image to appear if the displayed index exceeds 5. Adding % photoCount ensures that an image from the new vendor is displayed. Alternatively, we could watch changes in the vendor object and reset displayedPhoto to 0 when vendor changes.
  • In terms of swapping vendors, I have also implemented a more robust approach to managing the slider, ensuring that no interval is left running under any circumstances.

View the updated version here. Additional notes:

  • I had to simulate the axios request using a promise to approximate the actual response of the call.
  • I created a custom fader for images since the code for that was not provided

That covers the key updates made to the implementation.

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

How do I execute a Next.js script that utilizes `fs` and `sharp` during development with Webpack?

I'm working on creating a basic GIFPlayer that displays a GIF when the play button is clicked, and shows a PNG otherwise: <img className="w-full h-full" src={isPlaying ? gifPath : imgPath} alt={pic.alt} /> Since I only have a GIF file ...

Display error messages in Vue.js

Within this component, I have a method that updates an employee. My goal is to display the error message in the view immediately after the "errorMessage" variable is assigned/changed within the "error" callback of the ajax call. var EmployeeEdit = Vue ...

Tips for setting the scroll back to the top when switching between pages in quasar

Whenever a qlist item is clicked by the user, it redirects to another page. However, the scrolled position from the previous page is retained and not set to the top. This means that the user has to manually scroll back to the top to view the contents of th ...

Error: Unable to establish connection with local host (::1) on port 50106

I am currently in the process of developing a Discord bot using Node.js and erela.js. However, I encountered an error when attempting to retrieve the server that handles erela: A node error occurred: connect ECONNREFUSED ::1:50106 2020-05-01T21:23:19.367 ...

Local host's attempt to retrieve data from an external site via an Axios GET request was rejected

I'm currently attempting to execute a GET request on an external website in order to scrape some information. Unfortunately, my axios GET request is returning a connection error. I suspect that this issue may be related to the fact that I am sending t ...

Detecting the failure of chrome.extension.sendRequest

Greetings Chrome Developers! I am wondering how one can determine when a chrome.extension.sendRequest call has not been successful. I attempted the following approach with no luck: chrome.extension.sendRequest({ /* message stuff here */ }, function(req){ ...

Steps for importing a .vue file from a dependency:

In a Vue project I came across the following line of code at this GitHub link: import Viewer from 'v-viewer/src/component.vue'; I understand that v-viewer is a dependency, so it seems like src/component.vue is not part of the current project. Ho ...

Troubles arise when hovering over and connecting endpoints in jsPlumb

I'm currently facing two challenges with my project. Follow this link for more details. 1) The hover effect is working perfectly on the endpoints, but I can't seem to change the colors of my connector when hovering over it. Any suggestions? (Ref ...

"Unlock the secret to effortlessly redirecting users to a designated page when they click the browser's back

So I'm facing the challenge of disabling the browser back button on multiple routes and sending requests to the backend is resulting in inconsistent behavior. I don't want to create a multitude of similar API requests each time. Currently, I have ...

Transforming Form Input Fields into a Nested JSON Format with the Power of JQuery

I'm facing a challenge in converting the input fields below into a nested JSON file structure in my HTML page. Despite trying various methods, I haven't been successful yet. These inputs are retrieved through AngularJS ng-repeat. Any assistance o ...

Ways to incorporate vector .svg images into a D3js tree diagram

var treeData = [ { "name": "Top Level", "parent": "null", "remark":"yes", "children": [ { "name": "Level 2: A", "parent": "Top Level", "remark":"yes", "children": [ { "name": "So ...

What is the reason for the checkboxes in vuejs not being rendered with the checked attribute set

When working on an edit form, I encountered a situation where I had multiple options to choose from. These options were fetched via ajax using axios and assigned to the variable permisos in the component. Later, these options are rendered through a v-for l ...

Node.js server encountering a cross-domain error

As a beginner exploring node.js, I am embarking on setting up my very first installation. In my server.js file, I am defining a new server with the following code: var app = require('http').createServer(handler), io = require('socket.io&a ...

Creating a unique theme export from a custom UI library with Material-UI

Currently, I am in the process of developing a unique UI library at my workplace which utilizes Material-UI. This UI library features a custom theme where I have integrated custom company colors into the palette object. While these custom colors work perfe ...

JavaScript: A dynamic table is created with columns populated by JSON data. The row structure is compromised

On my webpage, I pull in two sets of JSON data: 1) warehouses and 2) items. After receiving an Ajax search response from my view, I need to dynamically generate the TDs of a table based on the warehouses information. The goal is to populate all TDs for ev ...

Creating a hierarchical tree in JavaScript and generating nested UL/LI output with ExpressJS

I have a piece of code from another request that is functioning properly. It creates a hierarchical tree with deep levels using JSON. However, I require the output to be in the form of an HTML UL/LI nested structure or a select menu for parent and child el ...

When setValue is called on VCheckbox in Vuetify, it emits an event called "update:modelValue"

After setting a value for the checkbox, I encountered a warning message: [Vue warn]: Component emitted event "update:modelValue" but it is neither declared in the emits option nor as an "onUpdate:modelValue" prop. Example.vue <script setup lang="t ...

Tips for securely integrating freelancers into your web project

Looking for a secure way to bring in freelancers to assist with tasks on our website, where the freelancer only has write access to specific pages. I'm aware that this can be done with tools like Windows Team Foundation Server. However, I need the fr ...

Should we retain the express variable for a specific purpose?

Being a developer who is still learning the ropes, I fail to understand the necessity of creating or retaining the express variable in an express/Node app. Instead of following this conventional approach: const express = require('express'); con ...

Submitting JSON data using JavaScript

My goal is to send a username and password to my backend server. However, I am encountering an issue where the data does not successfully reach the backend when using the following code: function register() { var text = '{"username":"admin1","pass ...