Vue.js Chrome Extension

To debug Vue.js applications you can add the DevTools extension for Chrome. Currently it supports only up to Vue.js version 3. You can get it from here. The problem with it is that it crashes when you load a lot of data into it. It's compatible with small Vue.js apps. You can build an extension for Chrome. We are making an extension that will display weather from OpenWeatherMap API. Run npx @vue/cli create weather-app to create a project. We will use vue-cli-plugin-browser-extension to add files for writing and compiling Chrome extension. Run vue add browser-extension to add the files needed to build the extension. To add redundant files remove App.vue and main.js from the src folder. Run npm run serve to build the files as we modify our code. Install Extension Reload to reload the extension since we are changing the files. Install it from here. Now go to chrome://extensions/URL in chrome and toggle turn on Developer Mode. You will notice a Load unpacked button click on it and select the dist folder in our project to load our extension into Chrome. Now we need to install some libraries to be used later. Axios for making HTTP requests, BootstrapVue for styling, Vee-Validate for form validation are these libraries. To install them run the npm i axios bootstrap-vue vee-validate command. Create currentWeather.vue in the components folder and add the following code.


<template>

  <div>

    <br />

    <b-list-group v-if="weather.main">

      <b-list-group-item>Current Temperature: {{weather.main.temp - 273.15}} C</b-list-group-item>

      <b-list-group-item>High: {{weather.main.temp_max - 273.15}} C</b-list-group-item>

      <b-list-group-item>Low: {{weather.main.temp_min - 273.15}} C</b-list-group-item>

      <b-list-group-item>Pressure: {{weather.main.pressure }}mb</b-list-group-item>

      <b-list-group-item>Humidity: {{weather.main.humidity }}%</b-list-group-item>

    </b-list-group>

  </div>

</template>

<script>

import { requestsMixin } from "@/mixins/requestsMixin";

export default {

  name: "CurrentWeather",

  mounted() {},

  mixins: [requestsMixin],

  computed: {

    keyword() {

      return this.$store.state.keyword;

    }

  },

  data() {

    return {

      weather: {}

    };

  },

  watch: {

    async keyword(val) {

      const response = await this.searchWeather(val);

      this.weather = response.data;

    }

  }

};

</script>

<style scoped>

p {

  font-size: 20px;

}

</style>


This component displays current weather from OpenWeatherMap API as the keyword from Vuex store is updated. Create Forecast.vue in the folder and add the code.


<template>

  <div>

    <br />

    <b-list-group v-for="(l, i) of forecast.list" :key="i">

      <b-list-group-item>

        <b>Date: {{l.dt_txt}}</b>

      </b-list-group-item>

      <b-list-group-item>Temperature: {{l.main.temp - 273.15}} C</b-list-group-item>

      <b-list-group-item>High: {{l.main.temp_max - 273.15}} C</b-list-group-item>

      <b-list-group-item>Low: {{l.main.temp_min }}mb</b-list-group-item>

      <b-list-group-item>Pressure: {{l.main.pressure }}mb</b-list-group-item>

    </b-list-group>

  </div>

</template>

<script>

import { requestsMixin } from "@/mixins/requestsMixin";

export default {

  name: "Forecast",

  mixins: [requestsMixin],

  computed: {

    keyword() {

      return this.$store.state.keyword;

    }

  },

  data() {

    return {

      forecast: []

    };

  },

  watch: {

    async keyword(val) {

      const response = await this.searchForecast(val);

      this.forecast = response.data;

    }

  }

};

</script>

<style scoped>

p {

  font-size: 20px;

}

</style>


Create mixins folder in src folder and add:


const APIURL = "http://api.openweathermap.org";

const axios = require("axios");

export const requestsMixin = {

  methods: {

    searchWeather(loc) {

      return axios.get(

        `${APIURL}/data/2.5/weather?q=${loc}&appid=${process.env.VUE_APP_APIKEY}`

      );

    },

    searchForecast(loc) {

      return axios.get(

        `${APIURL}/data/2.5/forecast?q=${loc}&appid=${process.env.VUE_APP_APIKEY}`

      );

    }

  }

};


These functions are for fetching current weather and forecast respectively from OpenWeatherMap API. process.env.VUE_APP_APIKEY is obtained from the .env file. In App.vue inside the popup folder replace the code with the following code.


<template>

  <div>

    <b-navbar toggleable="lg" type="dark" variant="info">

      <b-navbar-brand href="#">Weather App</b-navbar-brand>

    </b-navbar>

    <div class="page">

      <ValidationObserver ref="observer" v-slot="{ invalid }">

        <b-form @submit.prevent="onSubmit" novalidate>

          <b-form-group label="Keyword" label-for="keyword">

            <ValidationProvider name="keyword" rules="required" v-slot="{ errors }">

              <b-form-input

                :state="errors.length == 0"

                v-model="form.keyword"

                type="text"

                required

                placeholder="Keyword"

                name="keyword"

              ></b-form-input>

              <b-form-invalid-feedback :state="errors.length == 0">Keyword is required</b-form-invalid-feedback>

            </ValidationProvider>

          </b-form-group>

          <b-button type="submit" variant="primary">Search</b-button>

        </b-form>

      </ValidationObserver>

<br />

      <b-tabs>

        <b-tab title="Current Weather">

          <CurrentWeather />

        </b-tab>

        <b-tab title="Forecast">

          <Forecast />

        </b-tab>

      </b-tabs>

    </div>

  </div>

</template>

<script>

import CurrentWeather from "@/components/CurrentWeather.vue";

import Forecast from "@/components/Forecast.vue";

export default {

  name: "App",

  components: { CurrentWeather, Forecast },

  data() {

    return {

      form: {}

    };

  },

  methods: {

    async onSubmit() {

      const isValid = await this.$refs.observer.validate();

      if (!isValid) {

        return;

      }

      localStorage.setItem("keyword", this.form.keyword);

      this.$store.commit("setKeyword", this.form.keyword);

    }

  },

  beforeMount() {

    this.form = { keyword: localStorage.getItem("keyword") || "" };

  },

  mounted(){

    this.$store.commit("setKeyword", this.form.keyword);

  }

};

</script>

<style>

html {

  min-width: 500px;

}

.page {

  padding: 20px;

}

</style>


To show the extensions name we will add BootstrapVue b-navbar. When a user submits a number the onSubmit function is called and validationObserver becomes useful as it provides this.$refs.Observer.validate() function for form validation. If the isValid is true, the keyword is set in local storage and also in the Vuex store by running this.$store.commit(“setKeyword”, this.form.keyword); . Add the code in store.js.


import Vue from "vue";

import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({

  state: {

    keyword: ""

  },

  mutations: {

    setKeyword(state, payload) {

      state.keyword = payload;

    }

  },

  actions: {}

});


We can set keywords in our components since we have a keyword state for storing search keyword and setKeyword mutation. In popup/main.js add the code.


import Vue from 'vue'

import App from './App.vue'

import store from "../store";

import "bootstrap/dist/css/bootstrap.css";

import "bootstrap-vue/dist/bootstrap-vue.css";

import BootstrapVue from "bootstrap-vue";

import { ValidationProvider, extend, ValidationObserver } from "vee-validate";

import { required } from "vee-validate/dist/rules";

/* eslint-disable no-new */

extend("required", required);

Vue.component("ValidationProvider", ValidationProvider);

Vue.component("ValidationObserver", ValidationObserver);

Vue.use(BootstrapVue);

Vue.config.productionTip = false;

new Vue({

store,

  render: h => h(App)

}).$mount("#app");


The validation rules that we used in previous files are added here. In index.html copy paste the code below.


<!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>Weather App</title>

  </head>

  <body>

    <noscript>

      <strong

        >We're sorry but vue-chrome-extension-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>


That's all.