Обновлять элементы массива асинхронно - часы не запускаются - PullRequest
0 голосов
/ 17 мая 2018

Я создал компонент, который отображает предварительные просмотры статей в блогах.Этот компонент имеет нумерацию страниц, и при выборе новой страницы я обновляю массив предварительных просмотров статьи.Список статей извлекается из API-интерфейса JSON с сервера server1.Ответ содержит информацию для извлечения каждой статьи с сервера 2. Затем я запускаю x асинхронных выборок на сервер 2, столько же, сколько элементов в первом ответе.В этих ответах я обновляю элементы в массиве.

Я новичок в vue, но после некоторой борьбы заставил это работать.Теперь я пытаюсь добавить спиннер в превью статей, пока загружаются отдельные статьи.Моя идея состояла в том, чтобы посмотреть в компоненте предварительного просмотра обновления статьи и показать счетчик в зависимости от этого.К сожалению, это не работает, и теперь я начинаю сомневаться в моей реализации.Я заметил, что часы в предварительном просмотре не вызываются для каждого компонента предварительного просмотра, но каждый предварительный просмотр обновляется и отображается правильно.Я предполагаю, что это из-за системы обмена сообщениями, но мне не удается это исправить.

Мой вопрос состоит из двух частей:

  • Является ли моя реализация правильным способом решения этой проблемы?Чтобы заставить это работать, мне приятно «стереть» массив, потому что в противном случае новые статьи «перезаписывают» старые, и это было видно.
  • Как я могу справиться со счетчиками.Почему часы не срабатывают и как я могу это исправить?В приведенном ниже коде у меня есть некоторые записи консоли.Я вижу 10 раз «async» и каждый раз различное количество «watch», никогда 10.

Полный код на github здесь: Home и ArticlePreview.Это наиболее важные части:

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>

1 Ответ

0 голосов
/ 18 мая 2018

Я думаю, что метод получения подробных данных статьи должен быть инкапсулирован внутри компонента, а состояние загрузки также поддерживается внутри. Так же, как в коде ниже: (Он не работает должным образом, потому что Mockjs не может правильно работать вфрагмент)

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>
...