PWA  Tutorial

PWA Tutorial

1566116871

Build Your First PWA with Vue and TypeScript

With the ever-increasing use of mobile devices over the last few years it has become more and more important for web developers to anticipate the need for users on these devices. The first step was the ability to cater for different screen sizes, thus creating the need for responsive user interface design. Over time the demands of the users increase and it is now becoming even more important to provide a high-quality user experience, independent of the network connectivity. Users have become accustomed to using native installable applications when they are offline. They are increasingly expecting the same from browser-based web applications.

This expectation is met by Progressive Web Applications (or PWAs). A PWA is a normal web application that leverages a number of modern browser technologies to improve the overall experience. The core component of a PWA is a service worker. The service worker is a piece of JavaScript code that runs in a separate thread from the main JavaScript application and intercepts any browser requests for resources from the server. If the service worker finds that it has an up-to-date version of that resource in the cache it will provide the cached resource instead. In addition, an application manifest allows the application to be installed in the browser. This makes it possible to start up the PWA on a mobile device, even if the device is offline.

In this tutorial, I will show you how to develop a small PWA using the Vue framework. Vue is a framework that has been around for some time. It has recently gained in popularity as developers have come to realize that Vue strikes a good balance between a low-level hackability and high-level over-design. The application will allow the user to browse through a catalog of books. It will be making use of the OpenLibrary API to provide the data.

Create Your Vue Application

To start you will need to install the Vue command line tool. I will assume that you have some knowledge of JavaScript and the Node Package Manager (npm). I will also assume you have npm installed on your system. Open a shell and type the command:

npm install -g @vue/cli@3.7.0

This installs the global vue command. Depending on your system, you might have to run this command using sudo. Once the Vue command line tool has been installed you can create your first Vue application. Navigate into a directory of your choice and run the command

vue create vue-books-pwa

You will be prompted for a number of choices. In the first question, select Manually select features. This is important because you want to include the PWA features that Vue can install into a new application.

On the following prompt, you are presented with a number of choices. Make sure you select the Progressive Web App (PWA) Support and Router choices. You will be implementing the client using TypeScript, so you will also need to select the TypeScriptoption. Keep the Babel option selected. You may also want to deselect the Linter choice for this tutorial. In larger applications, I would suggest keeping the linter switched on to ensure a consistent code style across your application. Altogether the choices should look as follows.

? Check the features needed for your project:
 ◉ Babel
 ◉ TypeScript
❯◉ Progressive Web App (PWA) Support
 ◉ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◯ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

Once you have made your choices, press Enter to continue. When the wizard asks you Use history mode for router? you must answer no. For all other questions, simply accept the default options.

The vue create command will create a directory and fill it with a skeleton application. This application consists of an Appbase component and two routed components Home and About. All components are stored in .vue files.

A .vue file can contain three sections identified by XML tags: <template>, <style>, and <script>.

  • <template> - contains the HTML template that is used to render the component
  • <style> - contains any CSS that will be applied specifically to that component
  • <script lang="ts"> - contains the component’s logic implemented in TypeScript code

Before you start, implementing the components for the Book application, you will need to install some additional libraries that will be using throughout this tutorial. Navigate into the newly created vuew-books-pwa directory and run the following command.

cd vue-books-pwa
npm i vue-material@1.0.0-beta-10.2 axios@0.18.0 vue-axios@2.1.4

This will install the Material Design packages for Vue as well as the axios package that you will be using to create HTTP requests to the OpenLibrary API. Because you are using TypeScript, you will also need to install the type definitions for the Vue Material library. These have to be pulled from their GitHub repository. Run the command:

npm i git+https://github.com/calebsander/vue-material-types.git

To make use of the Material Design CSS styles and icons, open /public/index.html and add the following line to the <head> section.

<link href="https://fonts.googleapis.com/icon?family=Ubuntu|Material+Icons" rel="stylesheet">

The public/index.html file contains the application’s base HTML container into which Vue will render its output. The contents of the /public directory are served as static assets. The directory also contains favicon.ico which you might want to change for production.

The remainder of the application is contained in the /src directory. This is where all the code of your Vue components, their templates, and styles should be stored. In this directory, src/main.ts serves as the main entry point to the Vue application. Open this file and paste the following content into it after the import statements, keeping any default contents.

import axios from 'axios'
import VueAxios from 'vue-axios'
import VueMaterial from 'vue-material'
import 'vue-material/dist/vue-material.min.css'
import 'vue-material/dist/theme/default-dark.css'

Vue.use(VueMaterial);
Vue.use(VueAxios, axios);

The main component of the application is defined in src/App.vue. This file acts as the container for the routed components. Replace the contents of the file with the content below.

<template>
  <div id="app">
    <md-toolbar color="primary" class="expanded-toolbar">
      <span class="branding">
        <md-button><router-link to="/">{{title}}</router-link></md-button>
        <md-button><router-link to="/"><md-icon>home</md-icon></router-link></md-button>
      </span>
      <md-menu md-direction="bottom-start">
        <md-button md-menu-trigger><md-icon>menu</md-icon></md-button>
        <md-menu-content>
          <md-menu-item><router-link to="/">Home</router-link></md-menu-item>
          <md-menu-item><router-link to="/search">Search</router-link></md-menu-item>
        </md-menu-content>
      </md-menu>
    </md-toolbar>
    <router-view/>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default class App extends Vue {
  title = "Vue Books";
}
</script>

<style>
#app {
  font-family: 'Ubuntu', sans-serif;
}

.branding {
  flex: 1;
  text-align: left;
}

h1 {
  text-align: center;
}
</style>

The <md-topbar> element in the template defines the application’s top bar. It contains a menu with some links to the different sub-components. The splash screen is contained in src/views/Home.vue. Open it, and add a header and a sub-header.

<template>
  <div class="home">
    <h1>Vue Books PWA</h1>
    <h2>A simple progressive web application</h2>
  </div>
</template>

The default application created by vue-cli contains the About.vue component. You will not be using this component. Instead, the central component that provides the main functionality will be a component in which the user can search for books and view the search results in a table. Rename src/views/About.vue to src/views/Search.vue. Replace the contents with the following.

<template>
  <div class="search">
    <form v-on:submit.prevent="search">
      <div class="input-group">
        <md-field class="input-group-field">
          <label>Search</label>
          <md-input v-model="query"></md-input>
        </md-field>
        <div class="input-group-button"><md-button class="md-raised" v-on:click="search"><md-icon>search</md-icon></md-button></div>
      </div>
    </form>
    <h2>Search Results</h2>
    <md-table>
      <md-table-row>
        <md-table-head>Title</md-table-head>
        <md-table-head>Author</md-table-head>
        <md-table-head>Pub. Year</md-table-head>
        <md-table-head>View</md-table-head>
      </md-table-row>
      <md-table-row v-for="book in books" v-bind:key="book.key">
        <md-table-cell>{{book.title}}</md-table-cell>
        <md-table-cell>{{book.author_name && book.author_name.join(', ')}}</md-table-cell>
        <md-table-cell md-numeric>{{book.first_publish_year}}</md-table-cell>
        <md-table-cell><md-button v-on:click="viewDetails(book)"><md-icon>visibility</md-icon></md-button></md-table-cell>
      </md-table-row>
    </md-table>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import axios from 'axios';

@Component
export default class Search extends Vue {
  baseUrl = 'http://openlibrary.org';
  books = [];
  query = '';

  async search() {
    const response = await axios.get(this.baseUrl + `/search.json?title=${this.query}`);
    this.books = await response.data.docs;
  }

  viewDetails(book: any) {
    this.$router.push({ path: 'details', query: {
      title: book.title,
      authors: book.author_name && book.author_name.join(', '),
      year: book.first_publish_year,
      cover_id: book.cover_edition_key
    }});
  }
}
</script>

<style>
.input-group {
  margin-top: 1rem;
  display: flex;
  justify-content: center;
}

.input-group-field {
  margin-right: 0;
}

.input-group .input-group-button {
  margin-left: 0;
  border: none;
}

.input-group .md-raised {
  margin-top: 0;
  margin-bottom: 0;
  border-radius: 0;
}
</style>

This file contains quite a lot, so let’s discuss each section one by one. The top part contains the HTML template. This consists of a search form followed by a table that will display the results of a search.

The <script> segment of the search component contains the logic. It contains the search query and the results of the search in the books array. The component contains two methods. The search() method takes the search terms and performs a GET request to the OpenLibrary API.

When the result comes back, the books array is filled with the search results. The viewDetails method will cause the router to navigate to the Details component (which you will implement shortly). Each entry in the table contains a button linked to this method, allowing the user to view the book’s details. Finally, the third section in Search.vue contains some CSS styling.

The last component that needs implementing shows the book’s details. Create a new file src/views/Details.vue and fill it with the code below.

<template>
  <div class="details">
    <h1>Book Details</h1>
    <div class="content">
      <md-card class="details-card">
        <h3>{{book.title}}</h3>
        <img v-bind:src="getImageSrc()" />
        <h4>Authors</h4>
        <p> {{book.authors}} </p>
        <h4>Published</h4>
        <p>{{book.year}}</p>
      </md-card>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Details extends Vue {
  book: any;

  created() {
    this.book = {
      title: this.$route.query.title,
      cover_id: this.$route.query.cover_id,
      authors: this.$route.query.authors,
      year: this.$route.query.year
    };
  }

  getImageSrc() {
    return "http://covers.openlibrary.org/b/OLID/" + this.book.cover_id + "-M.jpg";
  }
}
</script>

<style>
.content {
  display: flex;
  justify-content: center;
}

.details-card {
  max-width: 800px;
  padding: 1rem 2rem;
}

.details-card p {
  padding-left: 2rem;
}
</style>

This component simply shows the book’s details obtained from the route’s query parameters. The only method, getImageSrc(), returns the URL of the cover image.

When the application was generated by the vue command line tool, it also created a HelloWorld component at src/components/HelloWorld.vue. This is not needed in the application, so you can delete it. If you delete this file, you’ll need to delete references to it in src/views/Home.vue as well.

In order for a sub-component to be shown, it must be registered with the router. Open src/router.ts and replace it with the code below.

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/search',
      name: 'search',
      component: () => import(/* webpackChunkName: "search" */ './views/Search.vue')
    },
    {
      path: '/details',
      name: 'details',
      component: () => import(/* webpackChunkName: "details" */ './views/Details.vue')
    }
  ]
})

export default router;

This completes the basic application. To try it out, you can run the command:

npm run serve

Open a browser and navigate to [http://localhost:8080](http://localhost:8080 "http://localhost:8080"). You can search for a book and click on the eye icon to look at the book’s details.

Add Secure Authentication to Your Vue PWA

In many situations, you will want to restrict access to parts of your application to users that are registered. You could start implementing your own user registration and sign-in mechanism. This is not only cumbersome but can leave you with security risks if the user registration is not tested properly. Fortunately, Okta provides a single sign-on service that lets you add safe user authentication with little effort. In this section, I will be showing you how to restrict access to the /search and /detailsroutes to registered users.

To start, you need to create an account with Okta. Visit developer.okta.com and click the Sign Up button. On the next screen, enter your details and click on Get Started.

Once you have finished the registration process, you will be taken to the developer dashboard. Each application that you want to use with Okta authentication must be registered and will receive its own client ID. Click on Add Application and, on the next screen, select Single Page Application. When you click on Next you will see a screen with settings. Make sure the port is set to 8080. This is the port that Vue uses to serve applications.

Once you are finished you will be given a clientId. This is needed in your application when configuring Okta. In your application directory now run the following command.

npm i @okta/okta-vue@1.1.0 @types/okta__okta-vue@1.0.2

This will install the Okta SDK for Vue. To set up Okta with your application, open src/router.ts. Add the following lines after the import statements.

import Auth from '@okta/okta-vue';

Vue.use(Auth, {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  client_id: '{yourClientId}',
  redirect_uri: window.location.origin + '/implicit/callback',
});

The Vue.use(Auth, ...) statement sets up Okta. You will need to copy the client ID from your Okta developer console as the client_id parameter.

In the routes array, add the following entry.

{
  path: '/implicit/callback',
  component: Auth.handleCallback()
}

This route will handle the callback from Okta, after the user has logged in.

Add a beforeEach() condition to the router at the bottom that sets up a redirect if authentication is required.

router.beforeEach(Vue.prototype.$auth.authRedirectGuard());

Finally, you have to add the authentication guards. In the router entries for the /search and /details, add the following property.

meta: {
  requiresAuth: true,
},

With this, your application is protected. If you now try to navigate to the /search route, you will be redirected to the Okta login page. In addition to protecting certain routes, the application should also let the user know if the user is logged in and provide a direct link to the Okta login page. Open src/App.vue. In the template section add the following into the <md-toolbar>, just after <span class="branding">.

<md-button v-if="authenticated" v-on:click="logout" id="logout-button"> Logout </md-button>
<md-button v-else v-on:click="$auth.loginRedirect()" id="login-button"> Login </md-button>

Replace the contents of the script section with the following.

import { Component, Vue, Watch } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  title = "Vue Books";
  public authenticated: boolean = false;

  private created() {
    this.isAuthenticated();
  }
  
  @Watch('$route')
  private async isAuthenticated() {
    this.authenticated = await this.$auth.isAuthenticated();
  }

  private async logout() {
    await this.$auth.logout();
    await this.isAuthenticated();

    // Navigate back to home
    this.$router.push({path: '/'});
  }
}

The flag authenticated keeps track of the login status. This controls the visibility of the Login and Logout buttons. This completes the implementation of the Vue Books application.

Create Your PWA in Vue

Until now, I have guided you through creating a standard web application. The only step towards creating a PWA was the choice to support PWAs during the initial set-up of the application. It turns out that this is almost everything that needs to be done. You can check the performance of the application using Google Chrome’s Lighthouse extension.

To test your application properly, you need to serve it in production mode. First, build the application by running the command:

npm run build

This will compile the application into the dist/ subdirectory. Next, you need to install the http-server-spa package by running the following command.

npm install -g http-server-spa@1.3.0

Then start the server by running:

http-server-spa dist index.html 8080

Open the Chrome browser and navigate to [http://localhost:8080](http://localhost:8080 "http://localhost:8080"). You can install the Lighthouse extension or use the Audits tab in Chrome Developer Tools to run Lighthouse.

If you have the extension installed, you will notice a little Lighthouse icon in the navigation bar. If you click on it a little panel will open. Select Generate Report and Lighthouse will start analyzing your application. There are a number of checks and you should get a score of 92 on the Progressive Web Application score. If you served the application using a secure server through HTTPS protocol you would likely score 100.

You could stop here and say that you have created a perfectly scoring PWA. But you can do a little better. If the application is modified to cache past search requests, a user can re-issue past searches and still get results, even if the device is offline. The axios-extensions library includes a caching layer that can be used out of the box. Install the extensions.

npm i axios-extensions@3.0.4

Open src/main.ts and add the following import.

import { cacheAdapterEnhancer } from 'axios-extensions';

Then replace Vue.use(VueAxios, axios) with the following.

Vue.use(VueAxios, axios.create({
  adapter: cacheAdapterEnhancer(axios.defaults.adapter as any)
}));

That’s it! You have created a PWA with Vue. A service worker caches access to the server resources. Requests to the external API are cached allowing the user to use the application without a network connection. The vue command line tool also created a manifest in public/manifest.json and a set of icons in public/img/icons. This allows the browser to install the application locally. For a production application, you should edit the manifest and update the icons.

Learn More about Vue and PWAs

This tutorial showed you how to create a PWA with Vue. PWAs are becoming increasingly popular in a world with more and more mobile devices with flaky internet connections. Vue is an excellent framework for developing web applications and makes it simple to add PWA features. As you have seen, adding authentication with Okta is pretty easy too.

You can find the source code for this tutorial on GitHub at oktadeveloper/okta-vue-books-pwa-example.

Further reading:

How to implement server-side pagination in Vue.js with Node.js

Apex Legends Tracker App | Full Stack Node & Vue.js

Use Schematics with Vue and Add Authentication in 5 Minutes

Data Visualization with Vue and D3.js

Ripple button with VueJS

Working with the Keyboard in your Vue App

With the ever-increasing use of mobile devices over the last few years it has become more and more important for web developers to anticipate the need for users on these devices. The first step was the ability to cater for different screen sizes, thus creating the need for responsive user interface design. Over time the demands of the users increase and it is now becoming even more important to provide a high-quality user experience, independent of the network connectivity. Users have become accustomed to using native installable applications when they are offline. They are increasingly expecting the same from browser-based web applications.

This expectation is met by Progressive Web Applications (or PWAs). A PWA is a normal web application that leverages a number of modern browser technologies to improve the overall experience. The core component of a PWA is a service worker. The service worker is a piece of JavaScript code that runs in a separate thread from the main JavaScript application and intercepts any browser requests for resources from the server. If the service worker finds that it has an up-to-date version of that resource in the cache it will provide the cached resource instead. In addition, an application manifest allows the application to be installed in the browser. This makes it possible to start up the PWA on a mobile device, even if the device is offline.

In this tutorial, I will show you how to develop a small PWA using the Vue framework. Vue is a framework that has been around for some time. It has recently gained in popularity as developers have come to realize that Vue strikes a good balance between a low-level hackability and high-level over-design. The application will allow the user to browse through a catalog of books. It will be making use of the OpenLibrary API to provide the data.

Create Your Vue Application

To start you will need to install the Vue command line tool. I will assume that you have some knowledge of JavaScript and the Node Package Manager (npm). I will also assume you have npm installed on your system. Open a shell and type the command:

npm install -g @vue/cli@3.7.0

This installs the global vue command. Depending on your system, you might have to run this command using sudo. Once the Vue command line tool has been installed you can create your first Vue application. Navigate into a directory of your choice and run the command

vue create vue-books-pwa

You will be prompted for a number of choices. In the first question, select Manually select features. This is important because you want to include the PWA features that Vue can install into a new application.

On the following prompt, you are presented with a number of choices. Make sure you select the Progressive Web App (PWA) Support and Router choices. You will be implementing the client using TypeScript, so you will also need to select the TypeScriptoption. Keep the Babel option selected. You may also want to deselect the Linter choice for this tutorial. In larger applications, I would suggest keeping the linter switched on to ensure a consistent code style across your application. Altogether the choices should look as follows.

? Check the features needed for your project:
 ◉ Babel
 ◉ TypeScript
❯◉ Progressive Web App (PWA) Support
 ◉ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◯ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

Once you have made your choices, press Enter to continue. When the wizard asks you Use history mode for router? you must answer no. For all other questions, simply accept the default options.

The vue create command will create a directory and fill it with a skeleton application. This application consists of an Appbase component and two routed components Home and About. All components are stored in .vue files.

A .vue file can contain three sections identified by XML tags: <template>, <style>, and <script>.

  • <template> - contains the HTML template that is used to render the component
  • <style> - contains any CSS that will be applied specifically to that component
  • <script lang="ts"> - contains the component’s logic implemented in TypeScript code

Before you start, implementing the components for the Book application, you will need to install some additional libraries that will be using throughout this tutorial. Navigate into the newly created vuew-books-pwa directory and run the following command.

cd vue-books-pwa
npm i vue-material@1.0.0-beta-10.2 axios@0.18.0 vue-axios@2.1.4

This will install the Material Design packages for Vue as well as the axios package that you will be using to create HTTP requests to the OpenLibrary API. Because you are using TypeScript, you will also need to install the type definitions for the Vue Material library. These have to be pulled from their GitHub repository. Run the command:

npm i git+https://github.com/calebsander/vue-material-types.git

To make use of the Material Design CSS styles and icons, open /public/index.html and add the following line to the <head> section.

<link href="https://fonts.googleapis.com/icon?family=Ubuntu|Material+Icons" rel="stylesheet">

The public/index.html file contains the application’s base HTML container into which Vue will render its output. The contents of the /public directory are served as static assets. The directory also contains favicon.ico which you might want to change for production.

The remainder of the application is contained in the /src directory. This is where all the code of your Vue components, their templates, and styles should be stored. In this directory, src/main.ts serves as the main entry point to the Vue application. Open this file and paste the following content into it after the import statements, keeping any default contents.

import axios from 'axios'
import VueAxios from 'vue-axios'
import VueMaterial from 'vue-material'
import 'vue-material/dist/vue-material.min.css'
import 'vue-material/dist/theme/default-dark.css'

Vue.use(VueMaterial);
Vue.use(VueAxios, axios);

The main component of the application is defined in src/App.vue. This file acts as the container for the routed components. Replace the contents of the file with the content below.

<template>
  <div id="app">
    <md-toolbar color="primary" class="expanded-toolbar">
      <span class="branding">
        <md-button><router-link to="/">{{title}}</router-link></md-button>
        <md-button><router-link to="/"><md-icon>home</md-icon></router-link></md-button>
      </span>
      <md-menu md-direction="bottom-start">
        <md-button md-menu-trigger><md-icon>menu</md-icon></md-button>
        <md-menu-content>
          <md-menu-item><router-link to="/">Home</router-link></md-menu-item>
          <md-menu-item><router-link to="/search">Search</router-link></md-menu-item>
        </md-menu-content>
      </md-menu>
    </md-toolbar>
    <router-view/>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default class App extends Vue {
  title = "Vue Books";
}
</script>

<style>
#app {
  font-family: 'Ubuntu', sans-serif;
}

.branding {
  flex: 1;
  text-align: left;
}

h1 {
  text-align: center;
}
</style>

The <md-topbar> element in the template defines the application’s top bar. It contains a menu with some links to the different sub-components. The splash screen is contained in src/views/Home.vue. Open it, and add a header and a sub-header.

<template>
  <div class="home">
    <h1>Vue Books PWA</h1>
    <h2>A simple progressive web application</h2>
  </div>
</template>

The default application created by vue-cli contains the About.vue component. You will not be using this component. Instead, the central component that provides the main functionality will be a component in which the user can search for books and view the search results in a table. Rename src/views/About.vue to src/views/Search.vue. Replace the contents with the following.

<template>
  <div class="search">
    <form v-on:submit.prevent="search">
      <div class="input-group">
        <md-field class="input-group-field">
          <label>Search</label>
          <md-input v-model="query"></md-input>
        </md-field>
        <div class="input-group-button"><md-button class="md-raised" v-on:click="search"><md-icon>search</md-icon></md-button></div>
      </div>
    </form>
    <h2>Search Results</h2>
    <md-table>
      <md-table-row>
        <md-table-head>Title</md-table-head>
        <md-table-head>Author</md-table-head>
        <md-table-head>Pub. Year</md-table-head>
        <md-table-head>View</md-table-head>
      </md-table-row>
      <md-table-row v-for="book in books" v-bind:key="book.key">
        <md-table-cell>{{book.title}}</md-table-cell>
        <md-table-cell>{{book.author_name && book.author_name.join(', ')}}</md-table-cell>
        <md-table-cell md-numeric>{{book.first_publish_year}}</md-table-cell>
        <md-table-cell><md-button v-on:click="viewDetails(book)"><md-icon>visibility</md-icon></md-button></md-table-cell>
      </md-table-row>
    </md-table>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import axios from 'axios';

@Component
export default class Search extends Vue {
  baseUrl = 'http://openlibrary.org';
  books = [];
  query = '';

  async search() {
    const response = await axios.get(this.baseUrl + `/search.json?title=${this.query}`);
    this.books = await response.data.docs;
  }

  viewDetails(book: any) {
    this.$router.push({ path: 'details', query: {
      title: book.title,
      authors: book.author_name && book.author_name.join(', '),
      year: book.first_publish_year,
      cover_id: book.cover_edition_key
    }});
  }
}
</script>

<style>
.input-group {
  margin-top: 1rem;
  display: flex;
  justify-content: center;
}

.input-group-field {
  margin-right: 0;
}

.input-group .input-group-button {
  margin-left: 0;
  border: none;
}

.input-group .md-raised {
  margin-top: 0;
  margin-bottom: 0;
  border-radius: 0;
}
</style>

This file contains quite a lot, so let’s discuss each section one by one. The top part contains the HTML template. This consists of a search form followed by a table that will display the results of a search.

The <script> segment of the search component contains the logic. It contains the search query and the results of the search in the books array. The component contains two methods. The search() method takes the search terms and performs a GET request to the OpenLibrary API.

When the result comes back, the books array is filled with the search results. The viewDetails method will cause the router to navigate to the Details component (which you will implement shortly). Each entry in the table contains a button linked to this method, allowing the user to view the book’s details. Finally, the third section in Search.vue contains some CSS styling.

The last component that needs implementing shows the book’s details. Create a new file src/views/Details.vue and fill it with the code below.

<template>
  <div class="details">
    <h1>Book Details</h1>
    <div class="content">
      <md-card class="details-card">
        <h3>{{book.title}}</h3>
        <img v-bind:src="getImageSrc()" />
        <h4>Authors</h4>
        <p> {{book.authors}} </p>
        <h4>Published</h4>
        <p>{{book.year}}</p>
      </md-card>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Details extends Vue {
  book: any;

  created() {
    this.book = {
      title: this.$route.query.title,
      cover_id: this.$route.query.cover_id,
      authors: this.$route.query.authors,
      year: this.$route.query.year
    };
  }

  getImageSrc() {
    return "http://covers.openlibrary.org/b/OLID/" + this.book.cover_id + "-M.jpg";
  }
}
</script>

<style>
.content {
  display: flex;
  justify-content: center;
}

.details-card {
  max-width: 800px;
  padding: 1rem 2rem;
}

.details-card p {
  padding-left: 2rem;
}
</style>

This component simply shows the book’s details obtained from the route’s query parameters. The only method, getImageSrc(), returns the URL of the cover image.

When the application was generated by the vue command line tool, it also created a HelloWorld component at src/components/HelloWorld.vue. This is not needed in the application, so you can delete it. If you delete this file, you’ll need to delete references to it in src/views/Home.vue as well.

In order for a sub-component to be shown, it must be registered with the router. Open src/router.ts and replace it with the code below.

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/search',
      name: 'search',
      component: () => import(/* webpackChunkName: "search" */ './views/Search.vue')
    },
    {
      path: '/details',
      name: 'details',
      component: () => import(/* webpackChunkName: "details" */ './views/Details.vue')
    }
  ]
})

export default router;

This completes the basic application. To try it out, you can run the command:

npm run serve

Open a browser and navigate to [http://localhost:8080](http://localhost:8080 "http://localhost:8080"). You can search for a book and click on the eye icon to look at the book’s details.

Add Secure Authentication to Your Vue PWA

In many situations, you will want to restrict access to parts of your application to users that are registered. You could start implementing your own user registration and sign-in mechanism. This is not only cumbersome but can leave you with security risks if the user registration is not tested properly. Fortunately, Okta provides a single sign-on service that lets you add safe user authentication with little effort. In this section, I will be showing you how to restrict access to the /search and /detailsroutes to registered users.

To start, you need to create an account with Okta. Visit developer.okta.com and click the Sign Up button. On the next screen, enter your details and click on Get Started.

Once you have finished the registration process, you will be taken to the developer dashboard. Each application that you want to use with Okta authentication must be registered and will receive its own client ID. Click on Add Application and, on the next screen, select Single Page Application. When you click on Next you will see a screen with settings. Make sure the port is set to 8080. This is the port that Vue uses to serve applications.

Once you are finished you will be given a clientId. This is needed in your application when configuring Okta. In your application directory now run the following command.

npm i @okta/okta-vue@1.1.0 @types/okta__okta-vue@1.0.2

This will install the Okta SDK for Vue. To set up Okta with your application, open src/router.ts. Add the following lines after the import statements.

import Auth from '@okta/okta-vue';

Vue.use(Auth, {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  client_id: '{yourClientId}',
  redirect_uri: window.location.origin + '/implicit/callback',
});

The Vue.use(Auth, ...) statement sets up Okta. You will need to copy the client ID from your Okta developer console as the client_id parameter.

In the routes array, add the following entry.

{
  path: '/implicit/callback',
  component: Auth.handleCallback()
}

This route will handle the callback from Okta, after the user has logged in.

Add a beforeEach() condition to the router at the bottom that sets up a redirect if authentication is required.

router.beforeEach(Vue.prototype.$auth.authRedirectGuard());

Finally, you have to add the authentication guards. In the router entries for the /search and /details, add the following property.

meta: {
  requiresAuth: true,
},

With this, your application is protected. If you now try to navigate to the /search route, you will be redirected to the Okta login page. In addition to protecting certain routes, the application should also let the user know if the user is logged in and provide a direct link to the Okta login page. Open src/App.vue. In the template section add the following into the <md-toolbar>, just after <span class="branding">.

<md-button v-if="authenticated" v-on:click="logout" id="logout-button"> Logout </md-button>
<md-button v-else v-on:click="$auth.loginRedirect()" id="login-button"> Login </md-button>

Replace the contents of the script section with the following.

import { Component, Vue, Watch } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  title = "Vue Books";
  public authenticated: boolean = false;

  private created() {
    this.isAuthenticated();
  }
  
  @Watch('$route')
  private async isAuthenticated() {
    this.authenticated = await this.$auth.isAuthenticated();
  }

  private async logout() {
    await this.$auth.logout();
    await this.isAuthenticated();

    // Navigate back to home
    this.$router.push({path: '/'});
  }
}

The flag authenticated keeps track of the login status. This controls the visibility of the Login and Logout buttons. This completes the implementation of the Vue Books application.

Create Your PWA in Vue

Until now, I have guided you through creating a standard web application. The only step towards creating a PWA was the choice to support PWAs during the initial set-up of the application. It turns out that this is almost everything that needs to be done. You can check the performance of the application using Google Chrome’s Lighthouse extension.

To test your application properly, you need to serve it in production mode. First, build the application by running the command:

npm run build

This will compile the application into the dist/ subdirectory. Next, you need to install the http-server-spa package by running the following command.

npm install -g http-server-spa@1.3.0

Then start the server by running:

http-server-spa dist index.html 8080

Open the Chrome browser and navigate to [http://localhost:8080](http://localhost:8080 "http://localhost:8080"). You can install the Lighthouse extension or use the Audits tab in Chrome Developer Tools to run Lighthouse.

If you have the extension installed, you will notice a little Lighthouse icon in the navigation bar. If you click on it a little panel will open. Select Generate Report and Lighthouse will start analyzing your application. There are a number of checks and you should get a score of 92 on the Progressive Web Application score. If you served the application using a secure server through HTTPS protocol you would likely score 100.

You could stop here and say that you have created a perfectly scoring PWA. But you can do a little better. If the application is modified to cache past search requests, a user can re-issue past searches and still get results, even if the device is offline. The axios-extensions library includes a caching layer that can be used out of the box. Install the extensions.

npm i axios-extensions@3.0.4

Open src/main.ts and add the following import.

import { cacheAdapterEnhancer } from 'axios-extensions';

Then replace Vue.use(VueAxios, axios) with the following.

Vue.use(VueAxios, axios.create({
  adapter: cacheAdapterEnhancer(axios.defaults.adapter as any)
}));

That’s it! You have created a PWA with Vue. A service worker caches access to the server resources. Requests to the external API are cached allowing the user to use the application without a network connection. The vue command line tool also created a manifest in public/manifest.json and a set of icons in public/img/icons. This allows the browser to install the application locally. For a production application, you should edit the manifest and update the icons.

Learn More about Vue and PWAs

This tutorial showed you how to create a PWA with Vue. PWAs are becoming increasingly popular in a world with more and more mobile devices with flaky internet connections. Vue is an excellent framework for developing web applications and makes it simple to add PWA features. As you have seen, adding authentication with Okta is pretty easy too.

You can find the source code for this tutorial on GitHub at oktadeveloper/okta-vue-books-pwa-example.

Further reading:

How to implement server-side pagination in Vue.js with Node.js

Apex Legends Tracker App | Full Stack Node & Vue.js

Use Schematics with Vue and Add Authentication in 5 Minutes

Data Visualization with Vue and D3.js

Ripple button with VueJS

Working with the Keyboard in your Vue App

#vue-js #typescript #pwa

What is GEEK

Buddha Community

Build Your First PWA with Vue and TypeScript
Luna  Mosciski

Luna Mosciski

1600583123

8 Popular Websites That Use The Vue.JS Framework

In this article, we are going to list out the most popular websites using Vue JS as their frontend framework.

Vue JS is one of those elite progressive JavaScript frameworks that has huge demand in the web development industry. Many popular websites are developed using Vue in their frontend development because of its imperative features.

This framework was created by Evan You and still it is maintained by his private team members. Vue is of course an open-source framework which is based on MVVM concept (Model-view view-Model) and used extensively in building sublime user-interfaces and also considered a prime choice for developing single-page heavy applications.

Released in February 2014, Vue JS has gained 64,828 stars on Github, making it very popular in recent times.

Evan used Angular JS on many operations while working for Google and integrated many features in Vue to cover the flaws of Angular.

“I figured, what if I could just extract the part that I really liked about Angular and build something really lightweight." - Evan You

#vuejs #vue #vue-with-laravel #vue-top-story #vue-3 #build-vue-frontend #vue-in-laravel #vue.js

PWA  Tutorial

PWA Tutorial

1566116871

Build Your First PWA with Vue and TypeScript

With the ever-increasing use of mobile devices over the last few years it has become more and more important for web developers to anticipate the need for users on these devices. The first step was the ability to cater for different screen sizes, thus creating the need for responsive user interface design. Over time the demands of the users increase and it is now becoming even more important to provide a high-quality user experience, independent of the network connectivity. Users have become accustomed to using native installable applications when they are offline. They are increasingly expecting the same from browser-based web applications.

This expectation is met by Progressive Web Applications (or PWAs). A PWA is a normal web application that leverages a number of modern browser technologies to improve the overall experience. The core component of a PWA is a service worker. The service worker is a piece of JavaScript code that runs in a separate thread from the main JavaScript application and intercepts any browser requests for resources from the server. If the service worker finds that it has an up-to-date version of that resource in the cache it will provide the cached resource instead. In addition, an application manifest allows the application to be installed in the browser. This makes it possible to start up the PWA on a mobile device, even if the device is offline.

In this tutorial, I will show you how to develop a small PWA using the Vue framework. Vue is a framework that has been around for some time. It has recently gained in popularity as developers have come to realize that Vue strikes a good balance between a low-level hackability and high-level over-design. The application will allow the user to browse through a catalog of books. It will be making use of the OpenLibrary API to provide the data.

Create Your Vue Application

To start you will need to install the Vue command line tool. I will assume that you have some knowledge of JavaScript and the Node Package Manager (npm). I will also assume you have npm installed on your system. Open a shell and type the command:

npm install -g @vue/cli@3.7.0

This installs the global vue command. Depending on your system, you might have to run this command using sudo. Once the Vue command line tool has been installed you can create your first Vue application. Navigate into a directory of your choice and run the command

vue create vue-books-pwa

You will be prompted for a number of choices. In the first question, select Manually select features. This is important because you want to include the PWA features that Vue can install into a new application.

On the following prompt, you are presented with a number of choices. Make sure you select the Progressive Web App (PWA) Support and Router choices. You will be implementing the client using TypeScript, so you will also need to select the TypeScriptoption. Keep the Babel option selected. You may also want to deselect the Linter choice for this tutorial. In larger applications, I would suggest keeping the linter switched on to ensure a consistent code style across your application. Altogether the choices should look as follows.

? Check the features needed for your project:
 ◉ Babel
 ◉ TypeScript
❯◉ Progressive Web App (PWA) Support
 ◉ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◯ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

Once you have made your choices, press Enter to continue. When the wizard asks you Use history mode for router? you must answer no. For all other questions, simply accept the default options.

The vue create command will create a directory and fill it with a skeleton application. This application consists of an Appbase component and two routed components Home and About. All components are stored in .vue files.

A .vue file can contain three sections identified by XML tags: <template>, <style>, and <script>.

  • <template> - contains the HTML template that is used to render the component
  • <style> - contains any CSS that will be applied specifically to that component
  • <script lang="ts"> - contains the component’s logic implemented in TypeScript code

Before you start, implementing the components for the Book application, you will need to install some additional libraries that will be using throughout this tutorial. Navigate into the newly created vuew-books-pwa directory and run the following command.

cd vue-books-pwa
npm i vue-material@1.0.0-beta-10.2 axios@0.18.0 vue-axios@2.1.4

This will install the Material Design packages for Vue as well as the axios package that you will be using to create HTTP requests to the OpenLibrary API. Because you are using TypeScript, you will also need to install the type definitions for the Vue Material library. These have to be pulled from their GitHub repository. Run the command:

npm i git+https://github.com/calebsander/vue-material-types.git

To make use of the Material Design CSS styles and icons, open /public/index.html and add the following line to the <head> section.

<link href="https://fonts.googleapis.com/icon?family=Ubuntu|Material+Icons" rel="stylesheet">

The public/index.html file contains the application’s base HTML container into which Vue will render its output. The contents of the /public directory are served as static assets. The directory also contains favicon.ico which you might want to change for production.

The remainder of the application is contained in the /src directory. This is where all the code of your Vue components, their templates, and styles should be stored. In this directory, src/main.ts serves as the main entry point to the Vue application. Open this file and paste the following content into it after the import statements, keeping any default contents.

import axios from 'axios'
import VueAxios from 'vue-axios'
import VueMaterial from 'vue-material'
import 'vue-material/dist/vue-material.min.css'
import 'vue-material/dist/theme/default-dark.css'

Vue.use(VueMaterial);
Vue.use(VueAxios, axios);

The main component of the application is defined in src/App.vue. This file acts as the container for the routed components. Replace the contents of the file with the content below.

<template>
  <div id="app">
    <md-toolbar color="primary" class="expanded-toolbar">
      <span class="branding">
        <md-button><router-link to="/">{{title}}</router-link></md-button>
        <md-button><router-link to="/"><md-icon>home</md-icon></router-link></md-button>
      </span>
      <md-menu md-direction="bottom-start">
        <md-button md-menu-trigger><md-icon>menu</md-icon></md-button>
        <md-menu-content>
          <md-menu-item><router-link to="/">Home</router-link></md-menu-item>
          <md-menu-item><router-link to="/search">Search</router-link></md-menu-item>
        </md-menu-content>
      </md-menu>
    </md-toolbar>
    <router-view/>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default class App extends Vue {
  title = "Vue Books";
}
</script>

<style>
#app {
  font-family: 'Ubuntu', sans-serif;
}

.branding {
  flex: 1;
  text-align: left;
}

h1 {
  text-align: center;
}
</style>

The <md-topbar> element in the template defines the application’s top bar. It contains a menu with some links to the different sub-components. The splash screen is contained in src/views/Home.vue. Open it, and add a header and a sub-header.

<template>
  <div class="home">
    <h1>Vue Books PWA</h1>
    <h2>A simple progressive web application</h2>
  </div>
</template>

The default application created by vue-cli contains the About.vue component. You will not be using this component. Instead, the central component that provides the main functionality will be a component in which the user can search for books and view the search results in a table. Rename src/views/About.vue to src/views/Search.vue. Replace the contents with the following.

<template>
  <div class="search">
    <form v-on:submit.prevent="search">
      <div class="input-group">
        <md-field class="input-group-field">
          <label>Search</label>
          <md-input v-model="query"></md-input>
        </md-field>
        <div class="input-group-button"><md-button class="md-raised" v-on:click="search"><md-icon>search</md-icon></md-button></div>
      </div>
    </form>
    <h2>Search Results</h2>
    <md-table>
      <md-table-row>
        <md-table-head>Title</md-table-head>
        <md-table-head>Author</md-table-head>
        <md-table-head>Pub. Year</md-table-head>
        <md-table-head>View</md-table-head>
      </md-table-row>
      <md-table-row v-for="book in books" v-bind:key="book.key">
        <md-table-cell>{{book.title}}</md-table-cell>
        <md-table-cell>{{book.author_name && book.author_name.join(', ')}}</md-table-cell>
        <md-table-cell md-numeric>{{book.first_publish_year}}</md-table-cell>
        <md-table-cell><md-button v-on:click="viewDetails(book)"><md-icon>visibility</md-icon></md-button></md-table-cell>
      </md-table-row>
    </md-table>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import axios from 'axios';

@Component
export default class Search extends Vue {
  baseUrl = 'http://openlibrary.org';
  books = [];
  query = '';

  async search() {
    const response = await axios.get(this.baseUrl + `/search.json?title=${this.query}`);
    this.books = await response.data.docs;
  }

  viewDetails(book: any) {
    this.$router.push({ path: 'details', query: {
      title: book.title,
      authors: book.author_name && book.author_name.join(', '),
      year: book.first_publish_year,
      cover_id: book.cover_edition_key
    }});
  }
}
</script>

<style>
.input-group {
  margin-top: 1rem;
  display: flex;
  justify-content: center;
}

.input-group-field {
  margin-right: 0;
}

.input-group .input-group-button {
  margin-left: 0;
  border: none;
}

.input-group .md-raised {
  margin-top: 0;
  margin-bottom: 0;
  border-radius: 0;
}
</style>

This file contains quite a lot, so let’s discuss each section one by one. The top part contains the HTML template. This consists of a search form followed by a table that will display the results of a search.

The <script> segment of the search component contains the logic. It contains the search query and the results of the search in the books array. The component contains two methods. The search() method takes the search terms and performs a GET request to the OpenLibrary API.

When the result comes back, the books array is filled with the search results. The viewDetails method will cause the router to navigate to the Details component (which you will implement shortly). Each entry in the table contains a button linked to this method, allowing the user to view the book’s details. Finally, the third section in Search.vue contains some CSS styling.

The last component that needs implementing shows the book’s details. Create a new file src/views/Details.vue and fill it with the code below.

<template>
  <div class="details">
    <h1>Book Details</h1>
    <div class="content">
      <md-card class="details-card">
        <h3>{{book.title}}</h3>
        <img v-bind:src="getImageSrc()" />
        <h4>Authors</h4>
        <p> {{book.authors}} </p>
        <h4>Published</h4>
        <p>{{book.year}}</p>
      </md-card>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Details extends Vue {
  book: any;

  created() {
    this.book = {
      title: this.$route.query.title,
      cover_id: this.$route.query.cover_id,
      authors: this.$route.query.authors,
      year: this.$route.query.year
    };
  }

  getImageSrc() {
    return "http://covers.openlibrary.org/b/OLID/" + this.book.cover_id + "-M.jpg";
  }
}
</script>

<style>
.content {
  display: flex;
  justify-content: center;
}

.details-card {
  max-width: 800px;
  padding: 1rem 2rem;
}

.details-card p {
  padding-left: 2rem;
}
</style>

This component simply shows the book’s details obtained from the route’s query parameters. The only method, getImageSrc(), returns the URL of the cover image.

When the application was generated by the vue command line tool, it also created a HelloWorld component at src/components/HelloWorld.vue. This is not needed in the application, so you can delete it. If you delete this file, you’ll need to delete references to it in src/views/Home.vue as well.

In order for a sub-component to be shown, it must be registered with the router. Open src/router.ts and replace it with the code below.

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/search',
      name: 'search',
      component: () => import(/* webpackChunkName: "search" */ './views/Search.vue')
    },
    {
      path: '/details',
      name: 'details',
      component: () => import(/* webpackChunkName: "details" */ './views/Details.vue')
    }
  ]
})

export default router;

This completes the basic application. To try it out, you can run the command:

npm run serve

Open a browser and navigate to [http://localhost:8080](http://localhost:8080 "http://localhost:8080"). You can search for a book and click on the eye icon to look at the book’s details.

Add Secure Authentication to Your Vue PWA

In many situations, you will want to restrict access to parts of your application to users that are registered. You could start implementing your own user registration and sign-in mechanism. This is not only cumbersome but can leave you with security risks if the user registration is not tested properly. Fortunately, Okta provides a single sign-on service that lets you add safe user authentication with little effort. In this section, I will be showing you how to restrict access to the /search and /detailsroutes to registered users.

To start, you need to create an account with Okta. Visit developer.okta.com and click the Sign Up button. On the next screen, enter your details and click on Get Started.

Once you have finished the registration process, you will be taken to the developer dashboard. Each application that you want to use with Okta authentication must be registered and will receive its own client ID. Click on Add Application and, on the next screen, select Single Page Application. When you click on Next you will see a screen with settings. Make sure the port is set to 8080. This is the port that Vue uses to serve applications.

Once you are finished you will be given a clientId. This is needed in your application when configuring Okta. In your application directory now run the following command.

npm i @okta/okta-vue@1.1.0 @types/okta__okta-vue@1.0.2

This will install the Okta SDK for Vue. To set up Okta with your application, open src/router.ts. Add the following lines after the import statements.

import Auth from '@okta/okta-vue';

Vue.use(Auth, {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  client_id: '{yourClientId}',
  redirect_uri: window.location.origin + '/implicit/callback',
});

The Vue.use(Auth, ...) statement sets up Okta. You will need to copy the client ID from your Okta developer console as the client_id parameter.

In the routes array, add the following entry.

{
  path: '/implicit/callback',
  component: Auth.handleCallback()
}

This route will handle the callback from Okta, after the user has logged in.

Add a beforeEach() condition to the router at the bottom that sets up a redirect if authentication is required.

router.beforeEach(Vue.prototype.$auth.authRedirectGuard());

Finally, you have to add the authentication guards. In the router entries for the /search and /details, add the following property.

meta: {
  requiresAuth: true,
},

With this, your application is protected. If you now try to navigate to the /search route, you will be redirected to the Okta login page. In addition to protecting certain routes, the application should also let the user know if the user is logged in and provide a direct link to the Okta login page. Open src/App.vue. In the template section add the following into the <md-toolbar>, just after <span class="branding">.

<md-button v-if="authenticated" v-on:click="logout" id="logout-button"> Logout </md-button>
<md-button v-else v-on:click="$auth.loginRedirect()" id="login-button"> Login </md-button>

Replace the contents of the script section with the following.

import { Component, Vue, Watch } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  title = "Vue Books";
  public authenticated: boolean = false;

  private created() {
    this.isAuthenticated();
  }
  
  @Watch('$route')
  private async isAuthenticated() {
    this.authenticated = await this.$auth.isAuthenticated();
  }

  private async logout() {
    await this.$auth.logout();
    await this.isAuthenticated();

    // Navigate back to home
    this.$router.push({path: '/'});
  }
}

The flag authenticated keeps track of the login status. This controls the visibility of the Login and Logout buttons. This completes the implementation of the Vue Books application.

Create Your PWA in Vue

Until now, I have guided you through creating a standard web application. The only step towards creating a PWA was the choice to support PWAs during the initial set-up of the application. It turns out that this is almost everything that needs to be done. You can check the performance of the application using Google Chrome’s Lighthouse extension.

To test your application properly, you need to serve it in production mode. First, build the application by running the command:

npm run build

This will compile the application into the dist/ subdirectory. Next, you need to install the http-server-spa package by running the following command.

npm install -g http-server-spa@1.3.0

Then start the server by running:

http-server-spa dist index.html 8080

Open the Chrome browser and navigate to [http://localhost:8080](http://localhost:8080 "http://localhost:8080"). You can install the Lighthouse extension or use the Audits tab in Chrome Developer Tools to run Lighthouse.

If you have the extension installed, you will notice a little Lighthouse icon in the navigation bar. If you click on it a little panel will open. Select Generate Report and Lighthouse will start analyzing your application. There are a number of checks and you should get a score of 92 on the Progressive Web Application score. If you served the application using a secure server through HTTPS protocol you would likely score 100.

You could stop here and say that you have created a perfectly scoring PWA. But you can do a little better. If the application is modified to cache past search requests, a user can re-issue past searches and still get results, even if the device is offline. The axios-extensions library includes a caching layer that can be used out of the box. Install the extensions.

npm i axios-extensions@3.0.4

Open src/main.ts and add the following import.

import { cacheAdapterEnhancer } from 'axios-extensions';

Then replace Vue.use(VueAxios, axios) with the following.

Vue.use(VueAxios, axios.create({
  adapter: cacheAdapterEnhancer(axios.defaults.adapter as any)
}));

That’s it! You have created a PWA with Vue. A service worker caches access to the server resources. Requests to the external API are cached allowing the user to use the application without a network connection. The vue command line tool also created a manifest in public/manifest.json and a set of icons in public/img/icons. This allows the browser to install the application locally. For a production application, you should edit the manifest and update the icons.

Learn More about Vue and PWAs

This tutorial showed you how to create a PWA with Vue. PWAs are becoming increasingly popular in a world with more and more mobile devices with flaky internet connections. Vue is an excellent framework for developing web applications and makes it simple to add PWA features. As you have seen, adding authentication with Okta is pretty easy too.

You can find the source code for this tutorial on GitHub at oktadeveloper/okta-vue-books-pwa-example.

Further reading:

How to implement server-side pagination in Vue.js with Node.js

Apex Legends Tracker App | Full Stack Node & Vue.js

Use Schematics with Vue and Add Authentication in 5 Minutes

Data Visualization with Vue and D3.js

Ripple button with VueJS

Working with the Keyboard in your Vue App

With the ever-increasing use of mobile devices over the last few years it has become more and more important for web developers to anticipate the need for users on these devices. The first step was the ability to cater for different screen sizes, thus creating the need for responsive user interface design. Over time the demands of the users increase and it is now becoming even more important to provide a high-quality user experience, independent of the network connectivity. Users have become accustomed to using native installable applications when they are offline. They are increasingly expecting the same from browser-based web applications.

This expectation is met by Progressive Web Applications (or PWAs). A PWA is a normal web application that leverages a number of modern browser technologies to improve the overall experience. The core component of a PWA is a service worker. The service worker is a piece of JavaScript code that runs in a separate thread from the main JavaScript application and intercepts any browser requests for resources from the server. If the service worker finds that it has an up-to-date version of that resource in the cache it will provide the cached resource instead. In addition, an application manifest allows the application to be installed in the browser. This makes it possible to start up the PWA on a mobile device, even if the device is offline.

In this tutorial, I will show you how to develop a small PWA using the Vue framework. Vue is a framework that has been around for some time. It has recently gained in popularity as developers have come to realize that Vue strikes a good balance between a low-level hackability and high-level over-design. The application will allow the user to browse through a catalog of books. It will be making use of the OpenLibrary API to provide the data.

Create Your Vue Application

To start you will need to install the Vue command line tool. I will assume that you have some knowledge of JavaScript and the Node Package Manager (npm). I will also assume you have npm installed on your system. Open a shell and type the command:

npm install -g @vue/cli@3.7.0

This installs the global vue command. Depending on your system, you might have to run this command using sudo. Once the Vue command line tool has been installed you can create your first Vue application. Navigate into a directory of your choice and run the command

vue create vue-books-pwa

You will be prompted for a number of choices. In the first question, select Manually select features. This is important because you want to include the PWA features that Vue can install into a new application.

On the following prompt, you are presented with a number of choices. Make sure you select the Progressive Web App (PWA) Support and Router choices. You will be implementing the client using TypeScript, so you will also need to select the TypeScriptoption. Keep the Babel option selected. You may also want to deselect the Linter choice for this tutorial. In larger applications, I would suggest keeping the linter switched on to ensure a consistent code style across your application. Altogether the choices should look as follows.

? Check the features needed for your project:
 ◉ Babel
 ◉ TypeScript
❯◉ Progressive Web App (PWA) Support
 ◉ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◯ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

Once you have made your choices, press Enter to continue. When the wizard asks you Use history mode for router? you must answer no. For all other questions, simply accept the default options.

The vue create command will create a directory and fill it with a skeleton application. This application consists of an Appbase component and two routed components Home and About. All components are stored in .vue files.

A .vue file can contain three sections identified by XML tags: <template>, <style>, and <script>.

  • <template> - contains the HTML template that is used to render the component
  • <style> - contains any CSS that will be applied specifically to that component
  • <script lang="ts"> - contains the component’s logic implemented in TypeScript code

Before you start, implementing the components for the Book application, you will need to install some additional libraries that will be using throughout this tutorial. Navigate into the newly created vuew-books-pwa directory and run the following command.

cd vue-books-pwa
npm i vue-material@1.0.0-beta-10.2 axios@0.18.0 vue-axios@2.1.4

This will install the Material Design packages for Vue as well as the axios package that you will be using to create HTTP requests to the OpenLibrary API. Because you are using TypeScript, you will also need to install the type definitions for the Vue Material library. These have to be pulled from their GitHub repository. Run the command:

npm i git+https://github.com/calebsander/vue-material-types.git

To make use of the Material Design CSS styles and icons, open /public/index.html and add the following line to the <head> section.

<link href="https://fonts.googleapis.com/icon?family=Ubuntu|Material+Icons" rel="stylesheet">

The public/index.html file contains the application’s base HTML container into which Vue will render its output. The contents of the /public directory are served as static assets. The directory also contains favicon.ico which you might want to change for production.

The remainder of the application is contained in the /src directory. This is where all the code of your Vue components, their templates, and styles should be stored. In this directory, src/main.ts serves as the main entry point to the Vue application. Open this file and paste the following content into it after the import statements, keeping any default contents.

import axios from 'axios'
import VueAxios from 'vue-axios'
import VueMaterial from 'vue-material'
import 'vue-material/dist/vue-material.min.css'
import 'vue-material/dist/theme/default-dark.css'

Vue.use(VueMaterial);
Vue.use(VueAxios, axios);

The main component of the application is defined in src/App.vue. This file acts as the container for the routed components. Replace the contents of the file with the content below.

<template>
  <div id="app">
    <md-toolbar color="primary" class="expanded-toolbar">
      <span class="branding">
        <md-button><router-link to="/">{{title}}</router-link></md-button>
        <md-button><router-link to="/"><md-icon>home</md-icon></router-link></md-button>
      </span>
      <md-menu md-direction="bottom-start">
        <md-button md-menu-trigger><md-icon>menu</md-icon></md-button>
        <md-menu-content>
          <md-menu-item><router-link to="/">Home</router-link></md-menu-item>
          <md-menu-item><router-link to="/search">Search</router-link></md-menu-item>
        </md-menu-content>
      </md-menu>
    </md-toolbar>
    <router-view/>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default class App extends Vue {
  title = "Vue Books";
}
</script>

<style>
#app {
  font-family: 'Ubuntu', sans-serif;
}

.branding {
  flex: 1;
  text-align: left;
}

h1 {
  text-align: center;
}
</style>

The <md-topbar> element in the template defines the application’s top bar. It contains a menu with some links to the different sub-components. The splash screen is contained in src/views/Home.vue. Open it, and add a header and a sub-header.

<template>
  <div class="home">
    <h1>Vue Books PWA</h1>
    <h2>A simple progressive web application</h2>
  </div>
</template>

The default application created by vue-cli contains the About.vue component. You will not be using this component. Instead, the central component that provides the main functionality will be a component in which the user can search for books and view the search results in a table. Rename src/views/About.vue to src/views/Search.vue. Replace the contents with the following.

<template>
  <div class="search">
    <form v-on:submit.prevent="search">
      <div class="input-group">
        <md-field class="input-group-field">
          <label>Search</label>
          <md-input v-model="query"></md-input>
        </md-field>
        <div class="input-group-button"><md-button class="md-raised" v-on:click="search"><md-icon>search</md-icon></md-button></div>
      </div>
    </form>
    <h2>Search Results</h2>
    <md-table>
      <md-table-row>
        <md-table-head>Title</md-table-head>
        <md-table-head>Author</md-table-head>
        <md-table-head>Pub. Year</md-table-head>
        <md-table-head>View</md-table-head>
      </md-table-row>
      <md-table-row v-for="book in books" v-bind:key="book.key">
        <md-table-cell>{{book.title}}</md-table-cell>
        <md-table-cell>{{book.author_name && book.author_name.join(', ')}}</md-table-cell>
        <md-table-cell md-numeric>{{book.first_publish_year}}</md-table-cell>
        <md-table-cell><md-button v-on:click="viewDetails(book)"><md-icon>visibility</md-icon></md-button></md-table-cell>
      </md-table-row>
    </md-table>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import axios from 'axios';

@Component
export default class Search extends Vue {
  baseUrl = 'http://openlibrary.org';
  books = [];
  query = '';

  async search() {
    const response = await axios.get(this.baseUrl + `/search.json?title=${this.query}`);
    this.books = await response.data.docs;
  }

  viewDetails(book: any) {
    this.$router.push({ path: 'details', query: {
      title: book.title,
      authors: book.author_name && book.author_name.join(', '),
      year: book.first_publish_year,
      cover_id: book.cover_edition_key
    }});
  }
}
</script>

<style>
.input-group {
  margin-top: 1rem;
  display: flex;
  justify-content: center;
}

.input-group-field {
  margin-right: 0;
}

.input-group .input-group-button {
  margin-left: 0;
  border: none;
}

.input-group .md-raised {
  margin-top: 0;
  margin-bottom: 0;
  border-radius: 0;
}
</style>

This file contains quite a lot, so let’s discuss each section one by one. The top part contains the HTML template. This consists of a search form followed by a table that will display the results of a search.

The <script> segment of the search component contains the logic. It contains the search query and the results of the search in the books array. The component contains two methods. The search() method takes the search terms and performs a GET request to the OpenLibrary API.

When the result comes back, the books array is filled with the search results. The viewDetails method will cause the router to navigate to the Details component (which you will implement shortly). Each entry in the table contains a button linked to this method, allowing the user to view the book’s details. Finally, the third section in Search.vue contains some CSS styling.

The last component that needs implementing shows the book’s details. Create a new file src/views/Details.vue and fill it with the code below.

<template>
  <div class="details">
    <h1>Book Details</h1>
    <div class="content">
      <md-card class="details-card">
        <h3>{{book.title}}</h3>
        <img v-bind:src="getImageSrc()" />
        <h4>Authors</h4>
        <p> {{book.authors}} </p>
        <h4>Published</h4>
        <p>{{book.year}}</p>
      </md-card>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Details extends Vue {
  book: any;

  created() {
    this.book = {
      title: this.$route.query.title,
      cover_id: this.$route.query.cover_id,
      authors: this.$route.query.authors,
      year: this.$route.query.year
    };
  }

  getImageSrc() {
    return "http://covers.openlibrary.org/b/OLID/" + this.book.cover_id + "-M.jpg";
  }
}
</script>

<style>
.content {
  display: flex;
  justify-content: center;
}

.details-card {
  max-width: 800px;
  padding: 1rem 2rem;
}

.details-card p {
  padding-left: 2rem;
}
</style>

This component simply shows the book’s details obtained from the route’s query parameters. The only method, getImageSrc(), returns the URL of the cover image.

When the application was generated by the vue command line tool, it also created a HelloWorld component at src/components/HelloWorld.vue. This is not needed in the application, so you can delete it. If you delete this file, you’ll need to delete references to it in src/views/Home.vue as well.

In order for a sub-component to be shown, it must be registered with the router. Open src/router.ts and replace it with the code below.

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/search',
      name: 'search',
      component: () => import(/* webpackChunkName: "search" */ './views/Search.vue')
    },
    {
      path: '/details',
      name: 'details',
      component: () => import(/* webpackChunkName: "details" */ './views/Details.vue')
    }
  ]
})

export default router;

This completes the basic application. To try it out, you can run the command:

npm run serve

Open a browser and navigate to [http://localhost:8080](http://localhost:8080 "http://localhost:8080"). You can search for a book and click on the eye icon to look at the book’s details.

Add Secure Authentication to Your Vue PWA

In many situations, you will want to restrict access to parts of your application to users that are registered. You could start implementing your own user registration and sign-in mechanism. This is not only cumbersome but can leave you with security risks if the user registration is not tested properly. Fortunately, Okta provides a single sign-on service that lets you add safe user authentication with little effort. In this section, I will be showing you how to restrict access to the /search and /detailsroutes to registered users.

To start, you need to create an account with Okta. Visit developer.okta.com and click the Sign Up button. On the next screen, enter your details and click on Get Started.

Once you have finished the registration process, you will be taken to the developer dashboard. Each application that you want to use with Okta authentication must be registered and will receive its own client ID. Click on Add Application and, on the next screen, select Single Page Application. When you click on Next you will see a screen with settings. Make sure the port is set to 8080. This is the port that Vue uses to serve applications.

Once you are finished you will be given a clientId. This is needed in your application when configuring Okta. In your application directory now run the following command.

npm i @okta/okta-vue@1.1.0 @types/okta__okta-vue@1.0.2

This will install the Okta SDK for Vue. To set up Okta with your application, open src/router.ts. Add the following lines after the import statements.

import Auth from '@okta/okta-vue';

Vue.use(Auth, {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  client_id: '{yourClientId}',
  redirect_uri: window.location.origin + '/implicit/callback',
});

The Vue.use(Auth, ...) statement sets up Okta. You will need to copy the client ID from your Okta developer console as the client_id parameter.

In the routes array, add the following entry.

{
  path: '/implicit/callback',
  component: Auth.handleCallback()
}

This route will handle the callback from Okta, after the user has logged in.

Add a beforeEach() condition to the router at the bottom that sets up a redirect if authentication is required.

router.beforeEach(Vue.prototype.$auth.authRedirectGuard());

Finally, you have to add the authentication guards. In the router entries for the /search and /details, add the following property.

meta: {
  requiresAuth: true,
},

With this, your application is protected. If you now try to navigate to the /search route, you will be redirected to the Okta login page. In addition to protecting certain routes, the application should also let the user know if the user is logged in and provide a direct link to the Okta login page. Open src/App.vue. In the template section add the following into the <md-toolbar>, just after <span class="branding">.

<md-button v-if="authenticated" v-on:click="logout" id="logout-button"> Logout </md-button>
<md-button v-else v-on:click="$auth.loginRedirect()" id="login-button"> Login </md-button>

Replace the contents of the script section with the following.

import { Component, Vue, Watch } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  title = "Vue Books";
  public authenticated: boolean = false;

  private created() {
    this.isAuthenticated();
  }
  
  @Watch('$route')
  private async isAuthenticated() {
    this.authenticated = await this.$auth.isAuthenticated();
  }

  private async logout() {
    await this.$auth.logout();
    await this.isAuthenticated();

    // Navigate back to home
    this.$router.push({path: '/'});
  }
}

The flag authenticated keeps track of the login status. This controls the visibility of the Login and Logout buttons. This completes the implementation of the Vue Books application.

Create Your PWA in Vue

Until now, I have guided you through creating a standard web application. The only step towards creating a PWA was the choice to support PWAs during the initial set-up of the application. It turns out that this is almost everything that needs to be done. You can check the performance of the application using Google Chrome’s Lighthouse extension.

To test your application properly, you need to serve it in production mode. First, build the application by running the command:

npm run build

This will compile the application into the dist/ subdirectory. Next, you need to install the http-server-spa package by running the following command.

npm install -g http-server-spa@1.3.0

Then start the server by running:

http-server-spa dist index.html 8080

Open the Chrome browser and navigate to [http://localhost:8080](http://localhost:8080 "http://localhost:8080"). You can install the Lighthouse extension or use the Audits tab in Chrome Developer Tools to run Lighthouse.

If you have the extension installed, you will notice a little Lighthouse icon in the navigation bar. If you click on it a little panel will open. Select Generate Report and Lighthouse will start analyzing your application. There are a number of checks and you should get a score of 92 on the Progressive Web Application score. If you served the application using a secure server through HTTPS protocol you would likely score 100.

You could stop here and say that you have created a perfectly scoring PWA. But you can do a little better. If the application is modified to cache past search requests, a user can re-issue past searches and still get results, even if the device is offline. The axios-extensions library includes a caching layer that can be used out of the box. Install the extensions.

npm i axios-extensions@3.0.4

Open src/main.ts and add the following import.

import { cacheAdapterEnhancer } from 'axios-extensions';

Then replace Vue.use(VueAxios, axios) with the following.

Vue.use(VueAxios, axios.create({
  adapter: cacheAdapterEnhancer(axios.defaults.adapter as any)
}));

That’s it! You have created a PWA with Vue. A service worker caches access to the server resources. Requests to the external API are cached allowing the user to use the application without a network connection. The vue command line tool also created a manifest in public/manifest.json and a set of icons in public/img/icons. This allows the browser to install the application locally. For a production application, you should edit the manifest and update the icons.

Learn More about Vue and PWAs

This tutorial showed you how to create a PWA with Vue. PWAs are becoming increasingly popular in a world with more and more mobile devices with flaky internet connections. Vue is an excellent framework for developing web applications and makes it simple to add PWA features. As you have seen, adding authentication with Okta is pretty easy too.

You can find the source code for this tutorial on GitHub at oktadeveloper/okta-vue-books-pwa-example.

Further reading:

How to implement server-side pagination in Vue.js with Node.js

Apex Legends Tracker App | Full Stack Node & Vue.js

Use Schematics with Vue and Add Authentication in 5 Minutes

Data Visualization with Vue and D3.js

Ripple button with VueJS

Working with the Keyboard in your Vue App

#vue-js #typescript #pwa

The Definitive Guide to TypeScript & Possibly The Best TypeScript Book

TypeScript Deep Dive

I've been looking at the issues that turn up commonly when people start using TypeScript. This is based on the lessons from Stack Overflow / DefinitelyTyped and general engagement with the TypeScript community. You can follow for updates and don't forget to ★ on GitHub 🌹

Reviews

  • Thanks for the wonderful book. Learned a lot from it. (link)
  • Its probably the Best TypeScript book out there. Good Job (link)
  • Love how precise and clear the examples and explanations are! (link)
  • For the low, low price of free, you get pages of pure awesomeness. Chock full of source code examples and clear, concise explanations, TypeScript Deep Dive will help you learn TypeScript development. (link)
  • Just a big thank you! Best TypeScript 2 detailed explanation! (link)
  • This gitbook got my project going pronto. Fluent easy read 5 stars. (link)
  • I recommend the online #typescript book by @basarat you'll love it.(link)
  • I've always found this by @basarat really helpful. (link)
  • We must highlight TypeScript Deep Dive, an open source book.(link)
  • Great online resource for learning. (link)
  • Thank you for putting this book together, and for all your hard work within the TypeScript community. (link)
  • TypeScript Deep Dive is one of the best technical texts I've read in a while. (link)
  • Thanks @basarat for the TypeScript Deep Dive Book. Help me a lot with my first TypeScript project. (link)
  • Thanks to @basarat for this great #typescript learning resource. (link)
  • Guyz excellent book on Typescript(@typescriptlang) by @basarat (link)
  • Leaning on the legendary @basarat's "TypeScript Deep Dive" book heavily at the moment (link)
  • numTimesPointedPeopleToBasaratsTypeScriptBook++; (link)
  • A book not only for typescript, a good one for deeper JavaScript knowledge as well. link
  • In my new job, we're using @typescriptlang, which I am new to. This is insanely helpful huge thanks, @basarat! link
  • Thank you for writing TypeScript Deep Dive. I have learned so much. link
  • Loving @basarat's @typescriptlang online book basarat.gitbooks.io/typescript/# loaded with great recipes! link
  • Microsoft doc is great already, but if want to "dig deeper" into TypeScript I find this book of great value link
  • Thanks, this is a great book 🤓🤓 link
  • Deep dive to typescript is awesome in so many levels. i find it very insightful. Thanks link
  • @basarat's intro to @typescriptlang is still one of the best going (if not THE best) link
  •  
  • This is sweet! So many #typescript goodies! link

Get Started

If you are here to read the book online get started.

Translations

Book is completely free so you can copy paste whatever you want without requiring permission. If you have a translation you want me to link here. Send a PR.

Other Options

You can also download one of the Epub, Mobi, or PDF formats from the actions tab by clicking on the latest build run. You will find the files in the artifacts section.

Special Thanks

All the amazing contributors 🌹

Share

Share URL: https://basarat.gitbook.io/typescript/

Author: Basarat
Source Code: https://github.com/basarat/typescript-book/ 
License: View license

#typescript #opensource 

Teresa  Bosco

Teresa Bosco

1598685221

Vue File Upload Using vue-dropzone Tutorial

In this tutorial, I will show you how to upload a file in Vue using vue-dropzone library. For this example, I am using Vue.js 3.0. First, we will install the Vue.js using Vue CLI, and then we install the vue-dropzone library. Then configure it, and we are ready to accept the file. DropzoneJS is an open source library that provides drag and drops file uploads with image previews. DropzoneJS is lightweight doesn’t depend on any other library (like jQuery) and is  highly customizable. The  vue-dropzone is a vue component implemented on top of Dropzone.js. Let us start Vue File Upload Using vue-dropzone Tutorial.

Dropzone.js is an open-source library providing drag-and-drop file uploads with image previews. DropzoneJS is lightweight, doesn’t depend on any other library (like jQuery), and is highly customizable.

The vue-dropzone is a vue component implemented on top of Dropzone.js.

First, install the Vue using Vue CLI.

Step 1: Install Vue.js using Vue CLI.

Go to your terminal and hit the following command.

npm install -g @vue/cli
         or
yarn global add @vue/cli

If you face any error, try running the command as an administrator.

Now, we need to generate the necessary scaffold. So type the following command.

vue create vuedropzone

It will install the scaffold.

Open the project in your favorite editor. Mine is Visual Studio Code.

cd vuedropzone
code .

Step 2: Install vue-dropzone.

I am using the Yarn package manager. So let’s install using Yarn. You can use NPM, also. It does not matter.

yarn add vue2-dropzone

or

npm install vue2-dropzone

Okay, now we need to add one css file with the above package. Now, vue cli uses css loader, so we can directly import in the src >>  main.js entry file.

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

import 'vue2-dropzone/dist/vue2Dropzone.css'

If importing css is not working for you, then you need to install that CSS file manually.

Copy this vue2Dropzone.css file’s content.

Create one file inside the src  >>  assets folder, create one css file called vuedropzone.css and paste the content there.

Import this css file inside src  >>  App.vue file.

<style lang="css">
  @import './assets/vuedropzone.css';
</style>

Now, it should include in our application.

Step 3: Upload an Image.

Our primary boilerplate has one ready-made component called HelloWorld.vue inside src  >>  components folder. Now, create one more file called FileUpload.vue.

Add the following code to FileUpload.vue file.

// FileUpload.vue

<template>
  <div id="app">
    <vue-dropzone id="upload" :options="config"></vue-dropzone>
  </div>
</template>

<script>
import vueDropzone from "vue2-dropzone";

export default {
  data: () => ({
    config: {
      url: "https://appdividend.com"
    }
  }),
  components: {
    vueDropzone
  }
};
</script>

Here, our API endpoint is https://appdividend.com. It is the point where we will hit the POST route and store our image, but it is my blog’s homepage, so it will not work anyway. But let me import this file into App.vue component and see what happens.

// App.vue

<template>
  <div id="app">
    <FileUpload />
  </div>
</template>

<script>
import FileUpload from './components/FileUpload.vue'

export default {
  name: 'app',
  components: {
    FileUpload
  }
}
</script>

<style lang="css">
  @import './assets/vuedropzone.css';
</style>

Now, start the development server using the following command. It will open up URL: http://localhost:8080.

npm run serve

Now, after uploading the image, we can see that the image upload is failed due to the wrong POST request endpoint.

Step 4: Create Laravel API for the endpoint.

Install the Laravel.

After that, we configure the database in the .env file and use MySQL database.

We need to create one model and migration file to store the image. So let us install the following command inside the Laravel project.

php artisan make:model Image -m

It will create both the Image model and create_images_table.php migrations file.

Now, open the migrations file and add the schema to it.

// create_images_table.php

public function up()
    {
        Schema::create('images', function (Blueprint $table) {
            $table->increments('id');
            $table->string('image_name');
            $table->timestamps();
        });
    }

Now, migrate the database table using the following command.

php artisan migrate

It creates the table in the database.

Now, we need to add a laravel-cors package to prevent cross-site-allow-origin errors. Go to the Laravel root and enter the following command to install it.

composer require barryvdh/laravel-cors

Configure it in the config  >>  app.php file.

Barryvdh\Cors\ServiceProvider::class,

Add the middleware inside app >>  Http  >>  Kernel.php file.

// Kernel.php

protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
        \Barryvdh\Cors\HandleCors::class,
];

Step 5: Define the API route and method to store the image.

First, create an ImageController.php file using the following command.

php artisan make:controller ImageController

Define the store method. Also, create one images folder inside the public directory because we will store an image inside it.

Right now, I have written the store function that handles one image at a time. So do not upload multiple photos at a time; otherwise, it will break.

// ImageController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Image;

class ImageController extends Controller
{
    public function store(Request $request)
    {
       if($request->file('file'))
       {
          $image = $request->file('file');
          $name = time().$image->getClientOriginalName();
          $image->move(public_path().'/images/', $name); 
        }

       $image= new Image();
       $image->image_name = $name;
       $image->save();

       return response()->json(['success' => 'You have successfully uploaded an image'], 200);
     }
}

Go to the routes   >>  api.php file and add the following route.

// api.php

Route::post('image', 'ImageController@store');

Step 6: Edit FileUpload.vue component.

We need to add the correct Post request API endpoint in FileUpload.vue component.

// FileUpload.vue

<template>
  <div id="app">
    <vue-dropzone id="drop1" :options="config" @vdropzone-complete="afterComplete"></vue-dropzone>
  </div>
</template>

<script>
import vueDropzone from "vue2-dropzone";

export default {
  data: () => ({
    config: {
      url: "http://localhost:8000/api/image",
      
    }
  }),
  components: {
    vueDropzone
  },
  methods: {
    afterComplete(file) {
      console.log(file);
    }
  }
};
</script>

Now, save the file and try to upload an image. If everything is okay, then you will be able to save the image on the Laravel web server as well as save the name in the database as well.

You can also verify on the server side by checking the database entry and the images folder in which we have saved the image.

Step 7: More vue-dropzone configuration.

The only required options are url, but there are many more you can use.

For example, let’s say you want:

  • A maximum of 4 files
  • 2 MB max file size
  • Sent in chunks of 500 bytes
  • Set a custom thumbnail size of 150px
  • Make the uploaded items cancelable and removable (by default, they’re not)
export default {
  data: () => ({
    dropOptions: {
      url: "https://httpbin.org/post",
      maxFilesize: 5, // MB
      maxFiles: 5,
      chunking: true,
      chunkSize: 400, // Bytes
      thumbnailWidth: 100, // px
      thumbnailHeight: 100,
      addRemoveLinks: true
    }
  })
  // ...
}

Happy Coding !!!

Originally published at https://appdividend.com 

#vue #vue-dropzone #vue.js #dropzone.js #dropzonejs #vue cli

Jack Downson

Jack Downson

1595928120

How to use Vue 3 with TypeScript to build powerful apps

Vue 3 with TypeScript

A demo project outlining how to use Vue 3 with TypeScript to build powerful apps.

What is used?

  • Vue 3
  • TypeScript 3.9
    • with type imports
  • Babel 7
  • Webpack 4
  • ESLint
  • Hot Module Replacement

Webpack 5 Demo

I have also built an example using webpack 5 which is still a work-in-progress. https://github.com/cshawaus/vue-3-ts-demo/tree/webpack-5

Todo

  • Fix vue/valid-template-root linting error
  • Ensure default ESLint rules work correctly for Vue 3

Download Details:

Author: cshawaus

GitHub: https://github.com/cshawaus/vue-3-ts-demo

#vuejs #javascript #vue #vue-js #typescript