Categories
Buefy

Buefy — Message Box

Buefy is a UI framework that’s based on Bulma.

In this article, we’ll look at how to use Buefy in our Vue app.

Message

We can add a message box with color with Buefy.

We can add it with the b-message component:

<template>
  <div id="app">
    <b-message title="Default" v-model="isActive">Lorem ipsum dolor sit amet</b-message>
  </div>
</template>

<script>
export default {
  name: "App",
  data(){
    return {
      isActive: true
    }
  }
};
</script>

title has the title.

v-model lets us control the open and close state.

The default slot has the content.

The color scheme can be changed with the type prop:

<template>
  <div id="app">
    <b-message type="is-success" title="Default" v-model="isActive">Lorem ipsum dolor sit amet</b-message>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      isActive: true
    };
  }
};
</script>

We can add an icon with the has-icon prop:

<template>
  <div id="app">
    <b-message
      type="is-success"
      has-icon
      title="Default"
      icon="address-book"
      icon-pack="fa"
      v-model="isActive"
    >Lorem ipsum dolor sit amet</b-message>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      isActive: true
    };
  }
};
</script>

We add the icon , icon-pack , and has-icon props to show the icon.

icon-pack is the icon pack name.

'fa' is Font Awesome.

icon has the icon class name.

In public/index.html , we add:

<link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
      integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
      crossorigin="anonymous"
    />

to the head tag to make the Font Awesome icons available.

The title prop is optional, so we can remove it to show a message box without the header:

<template>
  <div id="app">
    <b-message v-model="isActive">Lorem ipsum dolor sit amet</b-message>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      isActive: true
    };
  }
};
</script>

The size prop changes the size:

<template>
  <div id="app">
    <b-message size="is-large" v-model="isActive">Lorem ipsum dolor sit amet</b-message>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      isActive: true
    };
  }
};
</script>

The auto-close prop makes it close automatically:

<template>
  <div id="app">
    <b-message auto-close v-model="isActive">Lorem ipsum dolor sit amet</b-message>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      isActive: true
    };
  }
};
</script>

The duration that it’s visible can be set with the duration prop:

<template>
  <div id="app">
    <b-message :duration="2000" auto-close v-model="isActive">Lorem ipsum dolor sit amet</b-message>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      isActive: true
    };
  }
};
</script>

The number is in milliseconds.

Conclusion

We can add a message box with Buefy’s b-message component.

Categories
Buefy

Buefy — Loading Indicator and Menu

Buefy is a UI framework that’s based on Bulma.

In this article, we’ll look at how to use Buefy in our Vue app.

Loading Indicator

We can add a loading indicator with the b-loading component:

<template>
  <div id="app">
    <b-loading is-full-page v-model="isLoading" :can-cancel="true"></b-loading>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      isLoading: true
    };
  }
};
</script>

is-full-page sets the loading indicator to fill the page.

v-model binds to the isLoading state to let us control when it’s displayed.

can-cancel lets us close it with the Esc key with when it’s set to true .

We can open the loading indicator programmatically with the this.$buefy.loading.open method:

<template>
  <div id="app" ref="element">
    <button class="button is-primary is-medium" @click="open">load</button>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    open() {
      const loadingComponent = this.$buefy.loading.open({
        container: this.$refs.element.$el
      });
      setTimeout(() => loadingComponent.close(), 3 * 1000);
    }
  }
};
</script>

We call the method with the container property.

It sets the container for displaying the loading indicator.

We can set it to null to make it fill the screen.

The close method closes the loading indicator.

Also, we can create our own loading indicator by populating the default slot:

<template>
  <div id="app" ref="element">
    <b-loading is-full-page v-model="isLoading" :can-cancel="true">
      <b-icon pack="fa" icon="circle-o-notch" size="is-large" custom-class="fa-spin"></b-icon>
    </b-loading>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    isLoading: true
  }
};
</script>

We set the loading indicator to an icon with the b-icon component.

fa-spin makes Font Awesome icons spin.

Menu

Buefy comes with a menu component.

For example, we can write:

<template>
  <div id="app" ref="element">
    <b-menu>
      <b-menu-list label="Menu">
        <b-menu-item icon="address-book" icon-pack="fa" label="Info"></b-menu-item>
        <b-menu-item icon="settings" :active="isActive" expanded>
          <template slot="label" slot-scope="props">
            Administrator
            {{props.expanded ? '&#x2193;' : '&#x2191;'}}
          </template>
          <b-menu-item label="Users"></b-menu-item>
          <b-menu-item>
            <template slot="label">Devices
              <b-dropdown class="is-pulled-right" position="is-bottom-left">
                <b-icon slot="trigger"></b-icon>
                <b-dropdown-item>action 1</b-dropdown-item>
                <b-dropdown-item>action 2</b-dropdown-item>
              </b-dropdown>
            </template>
          </b-menu-item>
          <b-menu-item label="Payments" disabled></b-menu-item>
        </b-menu-item>
        <b-menu-item label="My Account">
          <b-menu-item label="Account data"></b-menu-item>
          <b-menu-item label="Addresses"></b-menu-item>
        </b-menu-item>
      </b-menu-list>
      <b-menu-list>
        <b-menu-item label="Expo" tag="router-link" target="_blank" to="/expo"></b-menu-item>
      </b-menu-list>
      <b-menu-list label="Actions">
        <b-menu-item label="Logout"></b-menu-item>
      </b-menu-list>
    </b-menu>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return { isActive: true };
  }
};
</script>

We add the b-menu to add the menu.

b-menu-item has the menu items.

b-dropdown adds dropdowns.

b-menu-list is a menu list to separate the menu into sections.

Conclusion

We can add a loading indicator and menu easily with Buefy.

Categories
Buefy

Buefy — Icons and Images

Buefy is a UI framework that’s based on Bulma.

In this article, we’ll look at how to use Buefy in our Vue app.

Icons

We can add icons with the b-icon component.

To do that, we add our icon CSS:

<link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
      integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
      crossorigin="anonymous"
    />

into our head tag of the public/index.html file.

Then in our component, we write:

<template>
  <div id="app">
    <b-icon icon="address-book" pack="fa" size="is-small"></b-icon>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

pack sets the icon pack to use.

'fa' sets it to Font Awesome.

icon is the icon name that we use.

size sets the size.

We can add the type ptop to set the style:

<template>
  <div id="app">
    <b-icon type="is-success" icon="address-book" pack="fa" size="is-small"></b-icon>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We can also write it as an object:

<template>
  <div id="app">
    <b-icon :type="{ 'is-success': true }" icon="address-book" pack="fa" size="is-small"></b-icon>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

This lets us set the style conditionally.

Image

Buefy comes with the b-image component to let us add images.

For example, we can write:

<template>
  <div id="app">
    <b-image src="https://picsum.photos/600/400" alt="image" ratio="6by4" rounded></b-image>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

alt sets the text description for the image.

src has the URL of the image.

rounded makes the image rounded.

ratio is the aspect ratio.

We can also add WebP images with b-image :

<template>
  <div id="app">
    <b-image
      src="https://picsum.photos/id/237/800/450.webp"
      webp-fallback="https://picsum.photos/id/1025/800/450.jpg"
      alt="image"
      ratio="6by4"
      rounded
    ></b-image>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

webp-fallback has the URL for the fallback image in case WebP images aren’t supported in your browser.

We can add srcset props to show different images when the screen is different sizes:

<template>
  <div id="app">
    <b-image
      src="https://picsum.photos/id/1074/1600/800"
      :srcset-sizes="[400, 800, 1600]"
      :srcset-formatter="this.formatSrcset"
      alt="image"
      ratio="6by4"
    ></b-image>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    formatSrcset(src, size) {
      return `https://picsum.photos/id/1000/${size}/${size / 2}`;
    }
  }
};
</script>

Also, we can listen to the load event and run an event handler when it loads:

<template>
  <div id="app">
    <b-image src="https://picsum.photos/id/1074/1600/800" alt="image" ratio="6by4" @load="onLoad"></b-image>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    onLoad(event, src) {
      console.log(src);
    }
  }
};
</script>

onLoad is run when the load event is emitted.

We can hamdle errors by listening to the error event and add a fallback image:

<template>
  <div id="app">
    <b-image
      src="https://picsum.photos/id/error/600/400"
      src-fallback="https://picsum.photos/id/237/600/400"
      ratio="6by4"
      @error="onError"
    ></b-image>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    onError(event, src) {
      console.log(`${src} fails to load`);
    }
  }
};
</script>

The onError method is run when the image set in the src attribute fails to load.

Conclusion

We can display icons and images easily with Buefy.

Categories
Nuxt.js

Adding Authentication to a Nuxt App with the Firebase

With the Nuxt Auth module, we can add authentication to our Nuxt app with ease.

One way is to add authentication with Firebase.

In this article, we’ll look at how to add Firebase authentication to our server-side rendered Nuxt app.

Install Packages

We have to add some packages to add Firebase to our Nuxt app.

To do that, we run:

npm i @nuxtjs/firebase @nuxtjs/pwa firebase firebase-admin

to add the required packages.

Configuration

We have to add configuration to our Nuxt app so that we can get the user when we load pages.

In nuxt.config.js , we write:

export default {
  /*
  ** Nuxt rendering mode
  ** See https://nuxtjs.org/api/configuration-mode
  */
  mode: 'universal',
  /*
  ** Nuxt target
  ** See https://nuxtjs.org/api/configuration-target
  */
  target: 'server',
  /*
  ** Headers of the page
  ** See https://nuxtjs.org/api/configuration-head
  */
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },
  /*
  ** Global CSS
  */
  css: [
  ],
  /*
  ** Plugins to load before mounting the App
  ** https://nuxtjs.org/guide/plugins
  */
  plugins: [
  ],
  /*
  ** Auto import components
  ** See https://nuxtjs.org/api/configuration-components
  */
  components: true,
  /*
  ** Nuxt.js dev-modules
  */
  buildModules: [
  ],
  /*
  ** Nuxt.js modules
  */
  modules: [
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    '@nuxtjs/pwa',
    [
      '@nuxtjs/firebase',
      {
        config: {
          apiKey: "api-key",
          authDomain: "project-id.firebaseapp.com",
          databaseURL: "https://project-id.firebaseio.com",
          projectId: "project-id",
          storageBucket: "project-id.appspot.com",
          messagingSenderId: 'message-sender-id',
          appId: "BookDb"
        },
        services: {
          auth: {
            persistence: 'local',
            initialize: {
              onAuthStateChangedMutation: "SET_USER",
              onAuthStateChangedAction: 'onAuthStateChangedAction',
            },
            ssr: {
              serverLogin: {
                sessionLifetime: 60 * 60 * 1000,
                loginDelay: 50
              }
            }
          }
        }
      }
    ]
  ],
  /*
  ** Axios module configuration
  ** See https://axios.nuxtjs.org/options
  */
  axios: {

},
  /*
  ** Build configuration
  ** See https://nuxtjs.org/api/configuration-build/
  */
  build: {
  },
  firebase: {
    services: {
      auth: {
        ssr: true
      }
    }
  },
  pwa: {
    meta: false,
    icon: false,
    workbox: {
      importScripts: [
        '/firebase-auth-sw.js'
      ],
      dev: true
    }
  }
}

We add the @nuxtjs/firebase module with many options.

The config property has the Firebase configuration options.

services configures the auth service to persist the user.

The onAuthStateChangeMutation action is the Vuex action to save the authenticated user’s data.

ssr has the options for how long to keep the user data.

The sessionLifetime property has the session lifetime.

And loginDelay is the delay to save the user data after a successful login.

Vuex Store

We’ve to create a Vuex store to store the data.

To do that, we create a store/index.js file and write:

export const state = () => ({
  authUser: {}
})

export const actions = {
  async onAuthStateChangedAction({ commit }, { authUser, claims }) {
    const { uid, email, emailVerified, displayName, photoURL } = authUser

  commit('SET_USER', {
      uid,
      email,
      emailVerified,
      displayName,
      photoURL,
      isAdmin: claims.custom_claim
    })
  },
  async nuxtServerInit({ dispatch, commit }, { res }) {
    console.log(res.locals)
    if (res && res.locals && res.locals.user) {
      const { allClaims: claims, idToken: token, ...authUser } = res.locals.user
      await dispatch('onAuthStateChangedAction', {
        authUser,
        claims,
        token
      })
      commit('ON_AUTH_STATE_CHANGED_MUTATION', { authUser, claims, token })
    }
  }
}

export const mutations = {
  ON_AUTH_STATE_CHANGED_MUTATION(state, { authUser, claims }) {
    const { uid, email, emailVerified, displayName, photoURL } = authUser

    state.authUser = {
      uid,
      displayName,
      email,
      emailVerified,
      photoURL: photoURL || null,
      isAdmin: claims.custom_claim
    }
  },
  SET_USER(state, payload) {
    console.log(payload)
    state.authUser = payload;
  }
}

The onAuthStateChanged action is invoked by nuxtServerInit to set the authenticated user’s data on page load.

nuxtServerInit is run when the page loads.

ON_AUTH_STATE_CHANGED_MUTATION is a mutation to save the authUser state.

SET_USER sets the authUser state.

When we run the this.$fireAuth.signInWithEmailAndPassword method is run successful by authentication with the correct credentials, then the actions and mutations will be run.

The res.locals.user property in the nuxtServerInit will also be set so that we have the currently logged in user’s data on page load.

Login Form

Finally, we need a login form so we can log in.

We create a login.vue file and add:

<template>
  <div class="container">
    <form @submit="signIn">
      <div>
        <label>Username</label>
        <input type="text" v-model="login.email" />
      </div>
      <div>
        <label>Password</label>
        <input type="text" v-model="login.password" />
      </div>
      <div>
        <button type="submit">Submit</button>
      </div>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      login: {},
    };
  },
  methods: {
    async signIn() {
      try {
        const { email, password } = this.login;
        await this.$fireAuth.signInWithEmailAndPassword(email, password);
      } catch (error) {
        console.log(error);
      }
    },
  },
};
</script>

We create a login form and if we log in with the right email and password, the Vuex actions will be run to set the data.

This is because the static/assets/firebase-auth-sw.js service worker that comes with the Nuxt Firebase does that work for us in the background.

Categories
Nuxt.js

Adding Authentication to a Nuxt App with the Nuxt Auth Module

With the Nuxt Auth module, we can add authentication to our Nuxt app with ease.

In this article, we’ll look at how to add authentication to our Nuxt app with Express and the Nuxt Auth module.

Getting Started

We’ve to add the Axios and Nuxt Auth modules.

Nuxt Auth uses Axios for sending requests.

To install it, we run:

yarn add @nuxtjs/auth @nuxtjs/axios

or:

npm install @nuxtjs/auth @nuxtjs/axios

Then in nuxt.config.js , we add:

const baseURL = "https://ReasonableRecursiveSupercollider--five-nine.repl.co";

export default {
  mode: 'universal',
  target: 'server',
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },
  css: [
  ],
  plugins: [
  ],
  components: true,
  buildModules: [
  ],
  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/auth'
  ],
  axios: {
    baseURL
  },
  build: {
  }
}

We add the baseURL to the axios property to set the base URL of our requests.

Also, we add the '@nuxtjs/auth' module to the modules array to add the module.

In the store folder, we’ve to add index.js to use the Nuxt auth module.

Back End

We use Express as the back end for our app.

To create it, we install Express with some packages by running:

npm i express body-parser cors

in a project folder.

Then we create index.js in the same folder and add:

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors')
const app = express();
app.use(cors())
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/api/auth/login', (req, res) => {
  res.json({});
});

app.post('/api/auth/logout', (req, res) => {
  res.json({});
});

app.get('/api/auth/user', (req, res) => {
  res.json({});
});

app.listen(3000, () => console.log('server started'));

We need the cors middleware to accept cross-domain requests.

The routes are the endpoints we send our auth requests to.

We should add logic to check for users in real-world applications.

Front End

In our Nuxt app, we add a login.vue component and add:

<template>
  <div class="container">
    <form @submit.prevent="userLogin">
      <div>
        <label>Username</label>
        <input type="text" v-model="login.username" />
      </div>
      <div>
        <label>Password</label>
        <input type="text" v-model="login.password" />
      </div>
      <div>
        <button type="submit">Submit</button>
      </div>
    </form>
  </div>
</template>

<script>
export default {
  middleware: "auth",
  data() {
    return {
      login: {},
    };
  },
  auth: {
    strategies: {
      local: {
        endpoints: {
          login: {
            url: `/api/auth/login`,
            method: "post",
            propertyName: "token",
          },
          logout: { url: `/api/auth/logout`, method: "post" },
          user: {
            url: `/api/auth/user`,
            method: "get",
            propertyName: "user",
          },
        },
      },
    },
  },
  methods: {
    async userLogin() {
      try {
        let response = await this.$auth.loginWith("local", {
          data: this.login,
        });
        console.log(response);
      } catch (err) {
        console.log(err);
      }
    },
  },
};
</script>

It has a login form that takes the username and password.

In the component options, we enable the auth middleware with the middleware: 'auth' property.

Then we set the auth options by adding the auth.strategies.local property to add the endpoints, we’ll make requests to.

login is used by the this.$auth.loginWith method to make requests.

auth.strategies.local optionally takes the tokenRequired and tokenType properties.

tokenRequired: false disables all token handling.

tokenType is the authorization header name to be used in Axios requests.

this.$auth.loginWith takes the strategy as the first argument and the data as the 2nd argument with the data property.

So when we submit the form, the userLogin method is called and the request to https://ReasonableRecursiveSupercollider--five-nine.repl.co/api/auth/login is made with the username and password in the request payload.

Conclusion

We can add a login form with the Nuxt and use the auth module to make the requests.