vue

添加Font Awesome到Vue.js应用程序

2019-11-13  本文已影响0人  开心人开发世界

Font Awesome是一个流行的CSS库,用于向Web应用程序添加图标。它可以作为原始CSS包提供,也有主要前端框架的版本。对于Vue.js,我们可以使用vue-fontawesome在我们的应用程序中添加图标。

在本文中,我们将制作一个购物清单应用程序,用户可以在其中使用MealDB API搜索想要烹饪的菜肴,并从中添加所需的食材。我们将在按钮上添加涟漪效果,并在鼠标指针移到按钮上并列出项目时使其突出显示。

要开始构建应用程序,我们可以通过运行来运行Vue CLI npx @vue/cli grocery-app。在向导中,选择“手动选择功能”,然后选择Babel,Vuex,Vue Router和CSS Preprocessor。

接下来,我们安装一些其他软件包。我们需要Axios来发出HTTP请求,需要BootstrapVue来样式化,需要Vue Font Awesome来添加图标,需要Vee-Validate来进行表单验证,并需要Vue Ripple指令来增加波纹效果。要安装它们,我们运行:

npm i axios @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/vue-fontawesome axios bootstrap-vue vee-validate vue-ripple-directive

安装所有软件包后,我们可以构建应用程序。我们首先添加用于添加食品的表格。GroceryForm.vue在components文件夹中创建一个文件并添加:

<template>
  <div>
    <ValidationObserver ref="observer" v-slot="{ invalid }">
      <b-form @submit.prevent="onSubmit" novalidate>
        <b-form-group label="Search for Dish and Add Ingredients From Result">
          <ValidationProvider name="keyword" rules="required" v-slot="{ errors }">
            <b-form-input
              type="text"
              :state="errors.length == 0"
              v-model="form.keyword"
              required
              placeholder="Search for Dish and Add Ingredients From Result"
              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 v-ripple.mouseover.500 type="submit" variant="primary">Find Dishes</b-button>
      </b-form>
    </ValidationObserver>
<b-card v-for="(m, i) in meals" :title="m.strMeal" :key="i">
      <b-card-text>
        <b-list-group>
          <b-list-group-item
            v-for="(key, index) in Object.keys(m).filter(k => k.includes('strIngredient') && m[k])"
            :key="index"
            v-ripple.mouseover="'rgba(255, 255, 255, 0.35)'"
          >{{m[key]}}</b-list-group-item>
        </b-list-group>
      </b-card-text>
      <b-button
        v-ripple.mouseover.500
        variant="primary"
        @click="addToGroceryList(i)"
      >Add Ingredients to Grocery List</b-button>
    </b-card>
<h4>Your Chosen Ingredients</h4>
    <b-list-group>
      <b-list-group-item v-for="(ingredient, i) of ingredients" :key="i" v-ripple.mouseover>
        {{ingredient}}
        <font-awesome-icon icon="times" class="float-right" @click="removeIngredient(i)" />
      </b-list-group-item>
    </b-list-group>
<br />
<b-button v-ripple.mouseover.500 type="button" variant="primary" @click="saveGroceryList()">Save</b-button>
    <b-button v-ripple.mouseover.500 type="reset" variant="danger" @click="cancel()">Cancel</b-button>
  </div>
</template>
<script>
import { requestsMixin } from "@/mixins/requestsMixin";
import { faTimes } from "@fortawesome/free-solid-svg-icons";
export default {
  name: "GroceryForm",
  mixins: [requestsMixin],
  components: {
    faTimes
  },
  data() {
    return {
      form: {},
      meals: [],
      ingredients: []
    };
  },
  computed: {
    grocery() {
      return this.$store.state.grocery;
    }
  },
  methods: {
    cancel() {
      this.$emit("cancelled");
    },
    async onSubmit() {
      const isValid = await this.$refs.observer.validate();
      if (!isValid) {
        return;
      }
      const { data } = await this.findDishes(this.form.keyword);
      this.meals = data.meals;
    },
    addToGroceryList(index) {
      const meal = this.meals[index];
      const keys = Object.keys(meal).filter(
        k => k.includes("strIngredient") && meal[k]
      );
      const ingredients = keys.map(k => meal[k]);
      this.ingredients = Array.from(
        new Set(this.ingredients.concat(ingredients))
      );
    },
    removeIngredient(index) {
      this.ingredients.splice(index, 1);
    },
    async saveGroceryList() {
      const payload = { ingredients: this.ingredients };
      if (!this.groceryListId) {
        await this.addGrocery(payload);
      } else {
        await this.editGrocery(payload);
      }
      const { data } = await this.getGrocery();
      this.$store.commit("setGrocery", data);
      this.$emit("saved");
    }
  },
  watch: {
    grocery: {
      handler(val) {
        this.ingredients = val.ingredients || [];
      },
      deep: true,
      immediate: true
    }
  }
};
</script>
<style lang="scss" scoped>
.delete {
  cursor: pointer;
}
</style>

该表单使用户可以使用给定的关键字搜索菜肴,然后返回菜肴的配料列表,然后用户可以将其添加到已删除重复项的列表中。我们使用Vee-Validate来验证我们的输入。我们使用ValidationObserver组件来监视组件内部表单的有效性,并ValidationProvider检查组件内部输入的输入值的验证规则。在中ValidationProvider,我们有用于文本输入字段的BootstrapVue输入。在b-form-input组件中。我们还添加了Vee-Validate验证,以确保用户在提交之前填写了日期。我们keywordrules道具中设置了必填字段,以便用户在搜索之前必须输入内容。

我们在表单底部的列表中有包含成分列表的按钮,用于删除每个成分。这就是为什么我们在faTimes此处导入图标的原因,该图标显示为“ x”,以便用户可以单击并删除它。如果单击该元素,removeIngredient则调用该函数。然后,用户单击表单底部的“保存”,然后saveGroceryList调用该函数,将列表保存到我们的后端。

在此组件中,我们还有一个watch块来监视grocery值,该块是从必须构建的Vuex存储中获得的。随着grocery值的更新,我们将获得最新的配料清单。

我们使用Vue Ripple将波纹效果应用到按钮和列表行。v-ripple.mouseover.500表示使用此指令将鼠标悬停在元素上时,纹波效果将显示500毫秒。要为波纹效果应用不同于默认颜色的颜色,我们还可以在伪指令的参数中指定颜色值,就像in中一样v-ripple.mouseover=”’rgba(255, 255, 255, 0.35)’”。波纹将具有指定的颜色。

我们使用样式设置元素以删除表单,cursor:pointer因此鼠标图标将显示一只手而不是箭头。

接下来,我们创建一个mixins文件夹并添加requestsMixin.js。在文件中:

const APIURL = "http://localhost:3000";
const MEAL_DB_URL = "https://www.themealdb.com/api/json/v1/1/search.php?s=";
const axios = require("axios");
export const requestsMixin = {
  methods: {
    getGrocery() {
      return axios.get(`${APIURL}/grocery`);
    },
addGrocery(data) {
      return axios.post(`${APIURL}/grocery`, data);
    },
editGrocery(data) {
      return axios.put(`${APIURL}/grocery`, data);
    },
findDishes(keyword) {
      return axios.get(`${MEAL_DB_URL}${keyword}`);
    }
  }
};

这些是我们在组件中使用的功能,用于发出HTTP请求以获取并保存我们的食品杂货数据并在Meal DB API中搜索菜肴。

接下来的中Home.vue,将现有代码替换为:

<template>
  <div class="page">
    <h1 class="text-center">Grocery List</h1>
    <b-button-toolbar class="button-toolbar">
      <b-button
        v-ripple.mouseover.500
        @click="openAddModal()"
        variant="primary"
      >Add Ingredients to Grocery List</b-button>
    </b-button-toolbar>
<h4>Your Grocery List</h4>
    <b-list-group>
      <b-list-group-item
        v-for="(ingredient, i) of grocery.ingredients"
        :key="i"
        v-ripple.mouseover="'rgba(255, 255, 255, 0.35)'"
      >
        {{ingredient}}
        <font-awesome-icon icon="times" class="float-right" @click="removeIngredient(i)" />
      </b-list-group-item>
    </b-list-group>
<b-modal id="add-modal" title="Add Ingredients to Grocery List" hide-footer>
      <GroceryForm
        @saved="closeModal()"
        @cancelled="closeModal()"
        :edit="false"
        :groceryListId="grocery.id"
      />
    </b-modal>
  </div>
</template>
<script>
// @ is an alias to /src
import GroceryForm from "@/components/GroceryForm.vue";
import { requestsMixin } from "@/mixins/requestsMixin";
export default {
  name: "home",
  components: {
    GroceryForm
  },
  mixins: [requestsMixin],
  computed: {
    grocery() {
      return this.$store.state.grocery;
    }
  },
  data() {
    return {
      ingredients: []
    };
  },
  beforeMount() {
    this.getGroceryList();
  },
  methods: {
    openAddModal() {
      this.$bvModal.show("add-modal");
    },
    closeModal() {
      this.$bvModal.hide("add-modal");
    },
    async getGroceryList() {
      const { data } = await this.getGrocery();
      this.$store.commit("setGrocery", data);
    },
    async removeIngredient(index) {
      this.ingredients.splice(index, 1);
      const payload = { id: this.grocery.id, ingredients: this.ingredients };
      await this.editGrocery(payload);
      const { data } = await this.getGrocery();
      this.$store.commit("setGrocery", data);
    }
  },
  watch: {
    grocery: {
      handler(val) {
        this.ingredients = val.ingredients || [];
      },
      deep: true,
      immediate: true
    }
  }
};
</script>
};

这是主页的组件。我们在这里显示从后端获得的所选配料列表。另外,我们还有一个按钮来打开一个模式,该模式与GroceryForm我们先前创建的一起添加到购物清单中。获取数据是在getGroceryList函数中完成的。我们将获得的数据放在函数的最后一行中的Vuex存储中。

此外,我们还允许用户使用该removeIngredient功能删除保存在此页面列表中的成分。我们调用从存储中的状态获得splicethis.ingredients数组,grocery然后将其设置为handlerwatch块中的当前值grocery

同样,我们使用Vue Ripple将波纹效果应用到按钮和列表行。v-ripple.mouseover.500显示按钮和按钮500毫秒的涟漪效应v-ripple.mouseover=”’rgba(255, 255, 255, 0.35)’”。像我们在中所做的那样,涟漪图将具有在列表项中指定的颜色GroceryForm

接下来的App.vue,我们将现有代码替换为:

<template>
  <div id="app">
    <b-navbar toggleable="lg" type="dark" variant="info">
      <b-navbar-brand to="/">Grocery List 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-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显示我们定义的路线。本style部分不受范围限制,因此样式将在全球范围内应用。在.page选择器中,我们向页面添加一些填充。我们在剩余style代码中为按钮添加一些填充。

然后在中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, min_value, max_value } from "vee-validate/dist/rules";
import Ripple from "vue-ripple-directive";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { library } from "@fortawesome/fontawesome-svg-core";
import { faTimes } from "@fortawesome/free-solid-svg-icons";
library.add(faTimes);
Vue.component("font-awesome-icon", FontAwesomeIcon);
Vue.directive("ripple", Ripple);
extend("required", required);
Vue.component("ValidationProvider", ValidationProvider);
Vue.component("ValidationObserver", ValidationObserver);
Vue.use(BootstrapVue);
Vue.config.productionTip = false;
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");

我们在此处添加了我们需要的所有库,包括BootstrapVue JavaScript和CSS,Vee-Validate组件以及验证规则,Vue-Ripple库和Vue Font Awesome软件包。使用Vue Font Awesome faTimeslibrary.add功能将其添加到我们的应用程序中,以便我们可以在我们的应用程序中使用它。

router.js我们将现有代码替换为:

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

这包括我们路线中的主页,以便用户可以看到该页面。

在中store.js,我们将现有代码替换为:

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
  state: {
    grocery: {}
  },
  mutations: {
    setGrocery(state, payload) {
      state.grocery = payload;
    }
  },
  actions: {}
});

这增加了我们的杂货店状态到店,所以我们可以观察它在computedGroceryFormHomePage组件。我们具有setGrocery更新grocery状态的功能,可以this.$store.commit(“setGrocery”, data);像在GroceryForm和中那样通过调用在组件中使用它HomePage

最后,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>Grocery List App</title>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but vue-ripple-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 start

要开始后端,我们首先json-server运行运行该软件包npm i json-server。然后,转到我们的项目文件夹并运行:

json-server --watch db.json

在中db.json,将文本更改为:

{
  "grocery": {}
}

因此,我们groceryrequests.js可用项中定义了端点。

经过所有的努力,我们得到:

翻译自:https://levelup.gitconnected.com/how-to-add-font-awesome-icons-to-your-vue-js-app-3641831887fd

上一篇下一篇

猜你喜欢

热点阅读