Asynchronous update of array elements - lack of firing watch events

I have recently developed a component that showcases previews of blog articles. This particular component includes pagination functionality, where selecting a new page triggers the refreshment of the article previews array. The list of articles is obtained from a JSON api on server1, which contains information necessary to fetch each individual article from server 2. Subsequently, x asynchronous fetches are fired off to server 2, matching the number of items in the first response. Upon receiving these responses, the items in the array are then updated.

Being relatively new to vue, I initially faced some challenges but managed to get everything working smoothly. Currently, my focus is on incorporating a spinner within the article previews while the separate articles are loading. My initial approach was to monitor an article update within the preview component and display the spinner accordingly. However, this does not seem to work as expected and has led me to question the implementation. Despite noticing that the watch in the preview component is not being called for every instance, the updates and displays function correctly. I suspect this inconsistency may be attributed to the messaging system, but so far, I haven't been able to rectify it.

My query encompasses two aspects:

  • Is my existing implementation an appropriate means of addressing this issue? To achieve seamless operation, I found it necessary to 'erase' the array to prevent new articles from 'overwriting' old ones, thus ensuring visibility.
  • How can I effectively manage the spinners? Why aren't the watches triggering, and what steps can be taken to resolve this matter? Within the provided code snippet below, you'll find some console logs detailing discrepancies observed during testing - specifically noting 'async' appearing 10 times and varying occurrences of 'watch', never consistently at 10 instances.

The complete code repository can be accessed on github via the following links: Home and ArticlePreview. These sections encompass the most crucial excerpts:

Home:

<template>
    <div class="container article">
        <div class="row" v-for="(article, index) in articles" :key="index">
            <ArticlePreview v-bind:blogEntry="article"></ArticlePreview>
        </div>
        <b-pagination-nav :use-router="true" :link-gen="generateLink" align="center" :number-of-pages="nofPages" v-model="pageIndex" />
    </div>
</template>

data: function ()
{
    return {
        articles: <BlogEntry[]> [],
        nofPages: 1
    }
},

loadContent()
{
    fetch("./api/v1/articles.php?page=" + this.pageIndex)
    .then(response => response.json())
    .then((data) =>
    {
        this.nofPages = Math.ceil(data.nofItems/10);
        this.articles.splice(0);
        this.articles.splice(data.data.length);
        let index :number;
        for(index = 0; index < data.data.length; index++)
        {
            createArticleAsync(data.data[index].name, data.data[index].permlink).then(function(this: any, index: number, article: BlogEntry)
            {
                console.log('async');
                    Vue.set(this.articles, index, article);
            }.bind(this, index));
        }
    })
},

ArticlePreview:

<template>
    <div class="container-fluid">
        <div class="row" v-if="blogEntry">
            <template v-if="blogEntry">
                <div class="imageframe col-md-3">
                    <div class="blog-image">
                        <img :src="blogEntry.previewImage" style="border-radius: 5px;">
                    </div>
                </div>
                <div class="col-md-9">
                    <h5 class="font-weight-bold" style="margin-top:5px;"><router-link :to="{ name: 'Article', params: {author: blogEntry.author, permlink: blogEntry.permlink } }">{{blogEntry.title}}</router-link></h5>
                    <div class="multiline-ellipsis">
                        <p>{{blogEntry.previewBody}}</p>
                    </div>
                    <span class="metadata"><i>by <a :href="AuthorBlogLink">{{blogEntry.author}}</a> on {{blogEntry.created | formatDate}}</i></span>
                </div>
            </template>
            <template v-else>
                <p>Loading</p>
            </template>
        </div>
    </div>
</template>

<script lang="ts">
    import Vue from "vue";
    import VueRouter from 'vue-router';

    import {formatDate} from "../utils/utils";

    export default Vue.extend({
        props: [
            'blogEntry'
        ],
        data: function ()
        {
            return {
                loading: true
            }
        },
        watch:
        {
            blogEntry(newValue)
            {
                console.log('watch');
                if(newValue)
                    this.loading = false;
                else
                    this.loading = true;
            }
        }
    });
</script>

Answer №1

It is important to encapsulate the method for obtaining detailed data of an article within the component itself, and also maintain the loading state internally. The following code demonstrates this concept (note that Mockjs may not execute properly in snippet):


Mock.setup({timeout: 2000})
const URL_ARTICLE_LIST = '/api/v1/articles.php'
const URL_ARTICLE_DETAIL = '/api/v1/article_detail.php'

Mock.mock(/\/api\/v1\/articles\.php.*/, function(options) {
  return {
    nofItems: 33,
    data: Mock.mock({
      'list|10': [{
        'title': '@title',
        'url': URL_ARTICLE_DETAIL
      }]
    }).list
  }
})

Mock.mock(URL_ARTICLE_DETAIL, function(options) {
  return Mock.mock({
    content: '@paragraph'
  })
})

Vue.component('article-card', {
  template: `
    <div>
      <template v-if="!loading">
        <div class="article-title">{{articleTitle}}</div>
        <div class="article-content">{{article.content}}</div>
      </template>
      <template v-else>
        <div>loading...</div>
      </template>
    </div>`,
  data () {
    return {
      loading: false,
      article: {}
    }
  },
  props: {
    articleTitle: {
      required: true,
      type: String
    },
    articleUrl: {
      required: true,
      type: String
    }
  },
  watch: {
    articleUrl (url, oldUrl) {
      if(url && url!=oldUrl){
        this.loadContent()
      }
    }
  },
  methods: {
    loadContent() {
      this.loading = true;
      //or your own async functions
      axios.get(this.articleUrl).then(res => {
        this.article = res.data
        this.loading = false;
      })
    }
  },
  created() {
    this.loadContent()
  }
});

var app = new Vue({
  el: '#app',
  data () {
    return {
      articles: [],
      nofPages: 1,
      page: 1 //you should get page param from vue-router just like this.$route.query.page  
    }
  },
  created () {
    //you can also use fetch here
    axios.get(URL_ARTICLE_LIST+'?page='+this.page).then(res => {
      console.log(res.data)
      this.nofPages = Math.ceil(res.data.nofItems/10);
      this.articles = res.data.data
    })
  }  
})

ul,li {
list-style: none;
}
.article_list {
display: flex;
flex-wrap: wrap;
}
.article_list > li {
width: 300px;
background: skyblue;
color: white;
margin: 10px;
}
.article-content {
text-indent: 2em;
}
.pagination-wrapper > li {
display: inline-block;
padding: 5px;
border: 1px solid skyblue;
margin: 3px;
}
.pagination-wrapper > li.active {
background: skyblue;
color: #fff;
}

<script src="https://cdn.bootcss.com/Mock.js/1.0.1-beta3/mock-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
  <ul class="article_list">
    <li v-for="article of articles">
      <article-card :article-title="article.title" :article-url="article.url"></article-card>
    </li>
  </ul>
  <ul class="pagination-wrapper">
  <li v-for="x in nofPages" :class="{active: page==x}">{{x}}</li>
  </ul>
</div>

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

Learn to save Canvas graphics as an image file with the powerful combination of fabric.js and React

I am currently utilizing fabric.js in a React application. I encountered an issue while attempting to export the entire canvas as an image, outlined below: The canvas resets after clicking the export button. When zoomed or panned, I am unable to export co ...

Encountering a problem with Chrome Extension localization upon installation - receiving an error message claiming default locale was not specified, despite having

Error Message: "The package has been deemed invalid due to the following reason: 'Localization was utilized, however default_locale was not specified in the manifest.' Issue: I have developed a customized extension and defined a default locale, ...

Progress to the following pages after each page remains inactive for 3 seconds

Is it possible for someone to assist me in creating a script that automatically switches between pages when the page is idle for 3 seconds? My current setup only allows for movement from one page to another, but I have 4 pages that I would like this featur ...

receiving unexpected data while using AJAX

For my PHP project, I have a function that validates a textbox using AJAX. The AJAX is working fine, but it only gives the following output for $return_value==-4 "<br /> <font size='1'><table class='xdebug-error xe-deprecated ...

What modifications need to be made to the MEAN app before it can be deployed on the server?

Following a tutorial on Coursetro, I was able to successfully build an Angular 4 MEAN stack application. However, when it comes to deploying the app on a server running on Debian-based OS, I am facing some challenges. The application should be accessible o ...

I'm not satisfied with the value of the ReactJs state after the change

I am working on creating a calendar app for practice purposes. Currently, I have a current_month state variable set to 1. Additionally, I have a function called IncreaseMonth() that is designed to increment the value of current_month. However, when the va ...

Failed to convert value to a string

I'm dealing with a frustrating issue and I just can't seem to figure it out. The error message I'm getting is Cast to string failed for value "{}" at path "post". { "confirmation": "fail", "message": { "message": "Cast to string fai ...

In order to activate the input switch in Next.Js, it is necessary to initiate a

Currently, I am working on implementing an on-off switch in Next.Js. To seek assistance, I referred to this helpful video tutorial: https://www.youtube.com/watch?v=1W3mAtAT7os&t=740s However, a recurring issue I face is that whenever the page reloads, ...

What is causing the continuous appearance of null in the console log?

As part of my JavaScript code, I am creating an input element that will be inserted into a div with the id "scripts" in the HTML. Initially, I add a value to this input field using JavaScript, and later I try to retrieve this value in another JavaScript fu ...

Searching for multiple values using ng-model in AngularJS is a powerful feature that allows

I've looked everywhere for a solution to my problem but I haven't found an exact match. Can anyone lend a hand? Here's the ng-repeat filter list I'm working with: <ul> <li ng-repeat="f in filterOptions"> <p>{{f ...

Uncovering the unique properties of custom Items in MUI v5 - RichTreeView: A Step-by-Step Guide

Can custom item properties received asynchronously with MUI - RichTreeView be exposed inside a custom treeItem? For instance, how can the customProperty1 and customProperty2 be accessed within the CustomTreeItem? The console.log to props only shows defaul ...

.htaccess configuration blocks execution of JavaScript in Vue single-page application

I have a Vue application that utilizes a router, and I have set up a route for resetting passwords in the form ../rp/:id. Here's the code snippet: export const routes = [ { path: '/rp/:id', name: 'resetPassword', comp ...

How can we set up Node JS to automatically trigger events when a specific future date and time from the database is reached?

I have taken on the challenge of providing users with a feature where they can select a date and time in the near future while placing an order. This chosen time and date will be saved in the database. How can I set up a function to automatically execute ...

I am facing an issue with incorporating dynamic imports in the configuration of my Vue router

Currently working on a Vue.js web page using webpack 4 and babel 6 for asset compilation. Encountering an issue when trying to use route('home', '/', () => import('../pages/home.vue')), as the compiler is throwing an error ...

Having trouble rendering JSON encoded data in a JqPlot Chart within a PHP script

I've spent the past few days scouring through Stack Overflow and various other websites, but I haven't been able to find a solution to my specific issue. Even the book 'Create Web Charts with JqPlot' by Fabio Nelli didn't provide t ...

JSON input in react.js sadly ended abruptly

I am encountering a problem when I attempt to submit the form. Unhandled Rejection (SyntaxError): Unexpected end of JSON input Whenever I press the submit button, this error occurs, here is the code snippet: onButtonSubmit = () => { this.setState({ ...

Utilize the $slots property when working with v-slot in your Vue application

When dealing with a specific use-case, it becomes necessary to retrieve the rendered width and height of a DOM element inside a slot. Typically, this can be achieved by accessing this.$slots.default[0].elm. However, complications arise when introducing sc ...

Problems with installing ambient typings

I'm having trouble installing some ambient typings on my machine. After upgrading node, it seems like the typings are no longer being found in the dt location. Here is the error message I am encountering: ~/w/r/c/src (master ⚡☡) typings search mo ...

What could be causing the removal of style classes from my element after it is appended?

Could you please explain how it functions? I am attempting to append a div with content using innerHTML and it is working, but without any of the styling classes being applied. const element = document.createElement("div"); element.classList.add("col ...

Dynamic Visibility Control of GridPanel Header in ExtJS

I have a GridPanel that needs to display a limited number of resources. If there are more resources available than what is currently shown, I would like the panel's header/title to indicate this by displaying text such as "more items available - displ ...