Part 3 - Build Steem blockchain application with Vue.js: using Bootstrap, nav component, mixins, and first iteration of Posts component

in #utopian-io5 years ago (edited)

Repository

Vue.js: https://github.com/vuejs/vue

What Will I Learn?

  • How to use Bootstrap in Vue.js
  • The nav component in Vue.js
  • Abstract common functionalities into mixins
  • Load posts from Steem blockchain into Posts component

Requirements

  • A Node.js environment (Ubuntu 18 + Node.js is used in this tutorial)
  • Basic HTML/CSS/Javascript knowledge

Difficulty

Basic level

Tutorial contents

In last tutorial, you have learned Vue.js components, computed properties, and the build and deployment process for Vue.js application. In this tutorial, you will learn how to use Bootstrap in Vue.js, how to use nav component in Vue.js, use mixins to abstract common functionalities, and load post information from Steem blockchain into Posts component.

The aim of this tutorial

As shown below, you will iterate your Vue.js application and add new functionalities: you will add three tabs, load posts from Steem blockchain, and use Bootstrap to control your Posts component layout.

The live demo is here: https://aa-feng.github.io/

Using Bootstrap in Vue.js

Bootstrap 4 is the world's most popular framework for building responsive, mobile-first sites.

First, to make your Vue.js application responsive and control the layout of your components, you will learn how to use Bootstrap in your Vue.js application.

Run this command to install bootstrap-vue and bootstrap packages:

npm i vue bootstrap-vue bootstrap --save

You also need to register BootstrapVue plugin in your application’s entry point, e.g. add the following code to main.js:

import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue)

Also, import the CSS files:

import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Now, you are ready to use Bootstrap in your components! You can layout your POST component like this:

<div class="row post">
  <div class="row post_header">
    <div class="col-sm-12">
      POST top bar
    </div>
  </div>

  <div class="row post_content">
    <div class="col-sm-2 col-xs-12 post_image">
      THUMBNAIL
    </div>
    <div  class="col-sm-10 col-xs-12 post_image">
      CONTENT
    </div>
  </div>
</div>

It looks like:

The nav component in Vue.js

In previous tutorial, we only have a very basic navigation bar. Actually, Vue.js provides a nice navigation component called ‘nav’. You can change the navigation by changing the code in App.vue to:

<template>
  <div id="app">
    <Profile></Profile>
    <b-nav tabs>
      <b-nav-item active><router-link to="/">Posts</router-link></b-nav-item>
      <b-nav-item><router-link to="/comments">Comments</router-link></b-nav-item>
      <b-nav-item><router-link to="/activities">Activities</router-link></b-nav-item>
    </b-nav>
    <router-view/>
  </div>
</template>

Now you will be able to see the navigation bar as shown below:

You may notice that some code refactoring has been done, e.g. the call to ‘Profile’ component has been moved to ‘App.vue’. The reason for that is because ‘Profile’ component is used by all three components, e.g. Posts, Comments, and Activities. Therefore, it is a better idea to call it in the parent component, e.g. in ‘App.vue’, rather than duplicate the calls in all these three components.

mixins

Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options.

Since user information, e.g. username, reputation are used in all components, you don't want to duplicate your code across all these components. Mixins is the perfect way to abstract common functionalities into a single place.

First, create file: src/components/mixins/user.js and add the following content:

let steem = require('steem')

export default {
  name: 'User',
  data () {
    return {
      username: 'aafeng', // default username
      profile: '', // Save user's jsonMetadata to profile
      userdata: '' // User's metadata
    }
  },
  created () {
    let names = [this.username]
    let currentComponent = this // Store this of current component (Profile) to currentComponent
    steem.api.getAccounts(names, function (err, result) { // retrieve data from Steem
      if (err) {
        console.log(err.stack)
      }
      currentComponent.userdata = result[0] // save the first user's data to userdata property
      var jsonMetadata = JSON.parse(result[0].json_metadata) // convert user's json_metadata to JSON
      currentComponent.profile = jsonMetadata.profile // save user's json_metadata to user's profile property
    })
  },
  computed: {
    voting_power () {
      return parseFloat(this.userdata.voting_power) / 100 // return a user friendly format of voting power
    },
    reputation () {
      return steem.formatter.reputation(this.userdata.reputation) // return a user friendly format of reputation
    }
  }
}

All above code are moved from ‘Profile.vue’. Then your ‘Profile.vue’ will be much simpler, e.g. the code will be:

<script>
import User from './mixins/user'

export default {
  name: 'Profile',
  mixins: [User]
}
</script>

In the above code, you need to import the User from mixins then you need to declare you want to use ‘User’ mixin in your ‘Profile’ component. The template stays as same as the previous version. In other components, you just need to follow the same rule if you want to use ‘User’ mixin.

First iteration of Posts component

To add posts information to Posts component, first you need to define a data attribute, e.g. posts, as shown below:

data () {
  return {
    posts: [] // user's posts
  }
}

Then, you can use Steem js to retrieve user’s posts from Steem blockchain, as shown below:

created () {
  let postComponent = this 
  steem.api.getDiscussionsByAuthorBeforeDate(this.username, null, new Date().toISOString().split('.')[0], 10, function (err, result) {
    if (err) {
      console.log(err.stack)
    }
    postComponent.posts = result
  })
}

You may notice that, for now, only a fixed number of posts are loaded, e.g. 10 latest posts are loaded.

For some information in the posts, e.g. the payouts (pending payouts / payouts), they might be used in other components, so it would be a good idea to define a new mixin, e.g. post mixin to include the common functionalities related to posts. You need to add a new file: src/components/mixins/posts.js with the following content:

export default {
  name: 'Post',

  methods: {
    // return pending payout or paid awards
    payout (post) {
      if (post.pending_payout_value !== '0.000 SBD') { // If it’s pending payout
        return post.pending_payout_value.replace(' SBD', '')
      } else { // if past payout, use total_payout_value
        return post.total_payout_value.replace(' SBD', '')
      }
    },
    firstImage (post) { // return first image URL from page content
      const regex = /(https?:\/\/.*\.(?:png|jpg|gif))/g // For now only check jpg/png/gif images
      let img = regex.exec(post.body)
      if (img === undefined) {
        return ''
      }
      return img[0]
    }
  }
}

In Posts component, both user and post mixins are needed, so declare them as below:

mixins: [User, Post],

Now it is the time to modify the template in Post.vue:

<div class="post_container">
  <div class="row post" v-for="post in posts" :key="post.post_id">
    <div class="row post_header">
      <div class="col-sm-12">
        {{username}}({{reputation}}) Category: {{ post.category }}
        Post time: {{ post.created }}
      </div>
    </div>
    <div class="row post_content">
      <div class="col-sm-2 col-xs-12 post_image">
        <img class="post_thumbnail" v-bind:src="firstImage(post)"/>
      </div>
      <div  class="col-sm-10 col-xs-12 post_image">
        <a v-bind:target="'_blank'" v-bind:href="'https://steemit.com'+post.url">{{ post.title }}</a><br/>

        Payout: ${{ payout(post) }}
        Votes: {{ post.active_votes.length }}
      </div>
    </div>
  </div>
</div>

As you can see, all methods defined in user and post mixins can be used in the template directly. Now, each post shows like below:

Curriculum

This is the third tutorial. More interesting topics will be covered in the following tutorials!

Previous tutorials

Part 1 - Build Steem blockchain application with Vue.js: installation and first demo
Part 2 - Build Steem blockchain application with Vue.js: components, computed properties and build/deployment process

Proof of Work Done

Source code for this tutorial: https://github.com/aa-feng/VuejsTutorial/tree/t03.1

Master branch of the source code (will be updated with ongoing tutorials): https://github.com/aa-feng/VuejsTutorial

The live demo of latest iteration: https://aa-feng.github.io/

Sort:  

I thank you for your contribution. Here are my thoughts. Note that, my thoughts are my personal ideas on your post and they are not directly related to the review and scoring unlike the answers I gave in the questionnaire;

  • Language

    • Usage of the first person makes tutorials harder to comprehend. If I were you, I would try my best to use the third person by changing the subjects of the sentences. I advise you to do that.
      For example;
      "The subject of the previous tutorial was the components of Vue.js, computed properties, building and deployment process of a Vue.js application."
      instead of
      "In last tutorial, you have learned Vue.js components, computed properties, and the build and deployment process for Vue.js application."
  • Content

    • I appreciate the comments in your code blocks. (Thanks for listening @portugalcoin's suggestion.)

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Chat with us on Discord.

[utopian-moderator]

Thank you for your suggestions.

Thank you for your review, @yokunjon! Keep up the good work!

Hi @aafeng!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Hey, @aafeng!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.31
TRX 0.11
JST 0.033
BTC 64550.89
ETH 3156.32
USDT 1.00
SBD 4.30