Индикатор выполнения - это строка, показывающая, насколько близко к завершению что-то в приложении с графическим интерфейсом. Он обеспечивает удобство работы пользователей, потому что они могут знать, когда что-то завершено и насколько близко к завершению, что делает умы пользователей более комфортными.

Для Vue.js создано множество библиотек индикаторов выполнения. Один из них - Vue-ProgressBar, расположенный по адресу https://github.com/hilongjw/vue-progressbar. Его легко включить в любое приложение Vue.js, и он очень гибкий, с множеством опций, которые вы можете изменить.

В этой статье мы создадим приложение, которое отображает шутки Чака Норриса из API шуток Чака Норриса, расположенного по адресу https://api.chucknorris.io/. Приложение будет иметь домашнюю страницу для отображения случайной шутки, страницу, которая позволяет пользователям искать случайную шутку по категориям, и страницу поиска для поиска шуток. Для начала запустим Vue CLI, запустив:

npx @vue/cli create chuck-norris-app

В мастере выбираем «Выбрать функции вручную» и выбираем Vue Router и Babel.

Далее мы устанавливаем несколько пакетов. Мы будем использовать Axios для выполнения HTTP-запросов, BootstrapVue для стилизации, Vue-ProgressBar для добавления индикатора выполнения и Vee-Validate для проверки формы. Для их установки запускаем:

npm i axios bootstrap-vue vue-progressbar vee-validate

Затем мы создаем папкуmixins в папке src и создаем файл с именем requestsMixin.js file. Там мы добавляем:

const APIURL = "https://api.chucknorris.io/jokes";
const axios = require("axios");
export const requestsMixin = {
  methods: {
    getRandomJoke() {
      return axios.get(`${APIURL}/random`);
    },
    getJokeByCategory(category) {
      return axios.get(`${APIURL}/random?category=${category}`);
    },
    getCategories() {
      return axios.get(`${APIURL}/categories`);
    },
    searchJokes(query) {
      return axios.get(`${APIURL}/search?query=${query}`);
    }
  }
};

В этом файле содержится код для вызова всех конечных точек API-интерфейса Чака Норриса для получения шуток и категорий, а также для поиска шуток по ключевым словам.

Затем в папке views мы заменяем код в файле Home.vue на:

<template>
  <div class="page">
    <h1 class="text-center">Random Joke</h1>
    <p>{{joke.value}}</p>
  </div>
</template>
<script>
import { requestsMixin } from "@/mixins/requestsMixin";
export default {
  name: "home",
  mixins: [requestsMixin],
  data() {
    return {
      joke: {}
    };
  },
  beforeMount() {
    this.$Progress.start();
    this.getJoke();
  },
  methods: {
    async getJoke() {
      const { data } = await this.getRandomJoke();
      this.joke = data;
      this.$Progress.finish();
    }
  }
};
</script>

С библиотекой Vue-ProgressBar у нас есть объект this.$Progress, доступный во всех наших компонентах, поскольку мы добавим его в main.js. Мы вызываем this.$Progress.start(); для отображения индикатора выполнения прямо перед выполнением HTTP-запроса, вызывая функцию this.getRandomJoke из requestsMixin. Затем, как только ответ будет успешно получен, мы вызываем this.$Progress.finish();, чтобы индикатор выполнения исчез. В шаблоне отображаем анекдот.

Затем создайте файл с именемJokeByCategory.vue в папке views и добавьте:

<template>
  <div class="page">
    <h1 class="text-center">Joke by Category</h1>
    <ValidationObserver ref="observer" v-slot="{ invalid }">
      <b-form novalidate>
        <b-form-group label="Category">
          <ValidationProvider name="category" rules="required" v-slot="{ errors }">
            <b-form-select v-model="category" :options="categories" @change="getJoke()"></b-form-select>
            <b-form-invalid-feedback :state="errors.length == 0">{{errors.join('. ')}}</b-form-invalid-feedback>
          </ValidationProvider>
        </b-form-group>
      </b-form>
    </ValidationObserver>
<p>{{joke.value}}</p>
  </div>
</template>
<script>
import { requestsMixin } from "@/mixins/requestsMixin";
export default {
  mixins: [requestsMixin],
  data() {
    return {
      category: "",
      categories: [],
      joke: {}
    };
  },
  beforeMount() {
    this.getJokeCategories();
  },
  methods: {
    async getJokeCategories() {
      this.$Progress.start();
      const { data } = await this.getCategories();
      this.categories = data.map(d => ({
        value: d,
        text: d
      }));
      this.$Progress.finish();
    },
    async getJoke() {
      this.$Progress.start();
      const isValid = await this.$refs.observer.validate();
      if (!isValid) {
        this.$Progress.finish();
        return;
      }
      const { data } = await this.getJokeByCategory(this.category);
      this.joke = data;
      this.$Progress.finish();
    }
  }
};
</script>

В ловушке beforeMount мы запускаем getJokeCategories, который вызывает this.getCategories из requestsMixin, чтобы получить категории при загрузке страницы.

Эта страница работает так же, как Home.vue. Мы отображаем индикатор выполнения при запуске запросов и удаляем его, когда запрос завершен. Этот файл выполняет 2 запроса: один для получения категорий из API с функцией this.categories из requestsMixin и функцией this.getJokesByCategory из того же файла.

В функции getJoke мы проверяем нашу форму с помощью Vee-Validate, вызывая this.$refs.observer.validate();, чтобы убедиться, что category выбрано, прежде чем получить шутку.

Мы используем Vee-Validate для проверки полей формы. Компонент ValidationObserver предназначен для проверки всей формы, а компонент ValidationProvider - для проверки полей формы, которые он обтекает.

Правило проверки определяется свойством rule поля category . Параметр state предназначен для установки состояния проверки, которое показывает зеленый цвет, когда errors имеет длину 0, и красный цвет в противном случае.

Сообщения об ошибках отображаются в компоненте b-form-invalid-feedback. На этой странице есть только раскрывающийся список стран.

Затем мы добавляем файл Search.vue в папку views и добавляем:

<template>
  <div class="page">
    <h1 class="text-center">Search</h1>
    <ValidationObserver ref="observer" v-slot="{ invalid }">
      <b-form novalidate @submit.prevent="onSubmit">
        <b-form-group label="Keyword">
          <ValidationProvider name="keyword" rules="required" v-slot="{ errors }">
            <b-form-input
              type="text"
              :state="errors.length == 0"
              v-model="keyword"
              required
              placeholder="Search "
              name="keyword"
            ></b-form-input>
            <b-form-invalid-feedback :state="errors.length == 0">{{errors.join('. ')}}</b-form-invalid-feedback>
          </ValidationProvider>
        </b-form-group>
<b-button type="submit" variant="primary">Search</b-button>
      </b-form>
    </ValidationObserver>
<p v-for="(j, i) of jokes" :key="i">{{j.value}}</p>
  </div>
</template>
<script>
import { requestsMixin } from "@/mixins/requestsMixin";
export default {
  mixins: [requestsMixin],
  data() {
    return {
      keyword: "",
      jokes: []
    };
  },
  methods: {
    async onSubmit() {
      this.$Progress.start();
      const isValid = await this.$refs.observer.validate();
      if (!isValid) {
        this.$Progress.finish();
        return;
      }
      const {
        data: { result }
      } = await this.searchJokes(this.keyword);
      this.jokes = result;
      this.$Progress.finish();
    }
  }
};
</script>

Мы позволяем пользователям искать анекдоты в API.

В функции onSubmit мы проверяем нашу форму с помощью Vee-Validate, вызывая this.$refs.observer.validate();, чтобы убедиться, что category выбрано, прежде чем получить шутку. Мы используем Vee-Validate для проверки полей формы. Компонент ValidationObserver предназначен для проверки всей формы, а компонент ValidationProvider предназначен для проверки полей формы, которые он обтекает. Правило проверки определяется опцией rule поля category . Параметр state предназначен для установки состояния проверки, которое показывает зеленый цвет, когда errors имеет длину 0, и красный цвет в противном случае. Сообщения об ошибках отображаются в компоненте b-form-invalid-feedback. На этой странице есть только раскрывающийся список стран.

Индикатор выполнения работает так же, как и другие компоненты. Мы отображаем индикатор выполнения при поиске шуток, вызывая this.$Progress.start();, затем this.searchJokes и удаляем его, когда запрос завершен. Наконец, вызывается this.$Progress.finish();, чтобы индикатор выполнения исчез.

Затем в App.vue мы заменяем существующий код на:

<template>
  <div id="app">
    <vue-progress-bar></vue-progress-bar>
    <b-navbar toggleable="lg" type="dark" variant="info">
      <b-navbar-brand to="/">Chuck Norris Jokes App</b-navbar-brand>
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
<b-collapse id="nav-collapse" is-nav>
        <b-navbar-nav>
          <b-nav-item to="/" :active="path  == '/'">Home</b-nav-item>
          <b-nav-item to="/jokebycategory" :active="path  == '/jokebycategory'">Jokes By Category</b-nav-item>
          <b-nav-item to="/search" :active="path  == '/search'">Search</b-nav-item>
        </b-navbar-nav>
      </b-collapse>
    </b-navbar>
    <router-view />
  </div>
</template>
<script>
export default {
  data() {
    return {
      path: this.$route && this.$route.path
    };
  },
  watch: {
    $route(route) {
      this.path = route.path;
    }
  }
};
</script>
<style lang="scss">
.page {
  padding: 20px;
}
button,
.btn.btn-primary {
  margin-right: 10px !important;
}
.button-toolbar {
  margin-bottom: 10px;
}
</style>

чтобы добавить панель навигации Bootstrap в верхнюю часть наших страниц и router-view для отображения определяемых нами маршрутов.

Затем в main.js замените код на:

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import BootstrapVue from "bootstrap-vue";
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-vue/dist/bootstrap-vue.css";
import { ValidationProvider, extend, ValidationObserver } from "vee-validate";
import { required } from "vee-validate/dist/rules";
import VueProgressBar from "vue-progressbar";
extend("required", required);
Vue.component("ValidationProvider", ValidationProvider);
Vue.component("ValidationObserver", ValidationObserver);
Vue.use(BootstrapVue);
Vue.use(VueProgressBar, {
  color: "rgb(143, 255, 199)",
  failedColor: "red",
  height: "2px"
});
Vue.config.productionTip = false;
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");

так что мы добавляем библиотеки, которые мы установили в наше приложение, чтобы мы могли использовать их в наших компонентах. Мы вызываем extend из Vee-Validate, чтобы добавить правила проверки формы, которые мы хотим использовать. Кроме того, мы добавляем сюда библиотеку Vue-ProgressBar, чтобы мы могли использовать ее во всех наших компонентах. Когда мы включаем его с Vue.use, мы передаем параметры индикатора выполнения в качестве второго аргумента. В этом приложении мы устанавливаем зеленоватый цвет, неудачный цвет - красный, а высоту - 2 пикселя. Мы также импортировали CSS Bootstrap в этот файл, чтобы получить стили.

В router.js мы заменяем существующий код на:

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import JokeByCategory from './views/JokeByCategory.vue'
import Search from './views/Search.vue'
Vue.use(Router)
export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/jokebycategory',
      name: 'jokebycategory',
      component: JokeByCategory
    },
    {
      path: '/search',
      name: 'search',
      component: Search
    }
  ]
})

чтобы включить нашу домашнюю страницу и страницу поиска.

Наконец, в index.html замените существующий код на:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <title>Chuck Norris Jokes App</title>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but vue-progress-bar-tutorial-app doesn't work properly
        without JavaScript enabled. Please enable it to continue.</strong
      >
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

изменить название.

После всей тяжелой работы мы можем запустить наше приложение, запустив npm run serve. В итоге получаем: