Thursday, 19 April, 2018 UTC


Summary

If you have ever used online stores like Amazon or eBay, you have definitely used the preview feature. It shows you images or videos of the product so you know what to expect before making a purchase.
In this article, we are going to examine how to build a single page Amazon-like preview app with Vue.js
What we will build won’t look exactly like the Amazon website, but will exhibit the product preview characteristics.
Dependencies
To build this app, we are going to use a Node server for the back-end and Vue.js for our front-end. Before we get to work, you need to verify that you have a couple of things installed on your machine:
  • Node
  • Node Package Manager(npm)
Building the Front-end
We going to use Vue.js to build the front-end. Vue.js is a progressive JavaScript framework that is quick and easy to use.
Installing Vue.js
You are going to need Vue.js installed on your machine. You can confirm your installation by running:
    vue --version
If you get a version number as a result then you have Vue.js installed. If not, it is recommended that you install the Vue CLI by running:
    npm install --global vue-cli
To create the frontend server, run the following :
    mkdir preview-app
    vue init webpack frontend
This creates a vue example project which we are now going to tweak and adjust.
Installing The Node Modules
We are going to use axios to make get requests from one of our Vue.js components so install it by running the following in the frontend directory:
    cd frontend
    npm install axios
Creating the Listing Component
This Listing component is responsible for showing all the products we have in the store and adding a link to the view for the product.
To create the Listing component, we run the following :
    touch Listing.vue
In the Listing.vue , we need to first import the axios module:
    <script>
    import axios from 'axios'
    //...
And now we use the module to fetch the product listing :
    //...
    export default {
      name: 'Listing',
      data () {
        return {
          products : []
        }
      },
      mounted : function(){
        axios.get('http://localhost:3128/products').
        then( result => {
          console.log( result );
          this.products = result.data;
        })
      }
    }
    </script>
We can see above that once the component is mounted, we make a call to our back-end server to fetch the available products and then assign it to the product data of the component.
The template for the component looks like this :
    <template>
      <div class="listing container">
        <div class="title" style="margin-bottom:40px;">
          <h1>Products on Sale</h1>
        </div>
        <div class="row">
          <div class="col-sm-2">
            <h2>#</h2>
          </div>
          <div class="col-sm-8">
            <h2>PRODUCT NAME</h2>
          </div>
          <div class="col-sm-2">
            <h2>GO TO</h2>
          </div>
        </div>

        <template v-for="product in products">
          <div class="row" style="margin-bottom:20px;">
            <div class="col-sm-2" >
              <p>{{ product.id }}</p>
            </div>
            <div class="col-sm-8">
              <p>{{ product.name }}</p>
            </div>
            <div class="col-sm-2">
              <router-link :to="{path: '/product/'+product.id }">View Product</router-link>
            </div>
          </div>
        </template>
      </div>
    </template>
In the template above, we list out the products as divs and add an action button that takes you to the single product page itself.
Creating the Preview Component
The Preview component is responsible for displaying data and images related to the selected product from the previous view. When the component is created, we make a get request to the backend server to fetch all the data for the particular id and then display the media in the form of a carousel on the right side of the screen.
Create the Preview.``v``ue file by running :
    touch Preview.vue
In the Vue.js file, we first import the axios module :
    <script>
    import axios from 'axios'
    //...
Now, we build the component :
    //...
    export default {
      name: 'Preview',
      data () {
        return {
          media :[],
          product_name : "",
          product_desc : "",
          product_price : ""
        }
      },
      mounted : function(){
        // now we get all the related infomation for the particular product id
        axios.get(`http://localhost:3128/getProductInfo/${this.$route.params.id}`)
        .then( res => {
          this.media = res.data.media;
          this.product_name = res.data.product_name;
          this.product_desc = res.data.product_desc;
          this.product_price = res.data.product_price;

        })
      },
      methods : {
        initializePlayer : function(){
          console.log('here')
          var cld = cloudinary.Cloudinary.new({ cloud_name: "og-tech", secure: true});
          var demoplayer = cld.videoPlayer('video-player');
        }
      }
    </script>
After the post request is made, the model is updated with the data that was returned as a JSON response on the back-end.
Our view has a template that looks as follows :
    <template>
      <div class="preview">
        <div class="row">
          <div class="col-sm-6">
            <!--  this part will contain the product info -->
            <h1> {{ product_name }} </h1>
            <div>
              <p> {{ product_desc }} </p>
              <p> Price : ${{ product_price }} </p>
            </div>
          </div>
          <div class="col-sm-6">
            <!--  this part will contain the images -->
            <div id="demo" class="carousel slide" data-ride="carousel">
              <!-- Indicators -->
              <ul class="carousel-indicators">
                <template v-for="single_media in media">
                  <template v-if="single_media.id == 0">
                    <li data-target="#demo" v-bind:data-slide-to="single_media.id" class="active"></li>
                  </template>
                  <template v-else>
                    <li data-target="#demo" v-bind:data-slide-to="single_media.id"></li>
                  </template>
                </template>
                <!-- <li data-target="#demo" data-slide-to="0" class="active"></li>
                <li data-target="#demo" data-slide-to="2"></li> -->
              </ul>
              <!-- The slideshow -->
              <div class="carousel-inner">
                <template v-for="single_media in media">
                  <template v-if="single_media.id == 0">
                    <div class="carousel-item active">
                      <template v-if="single_media.type == 'image'">
                        <img class="img-responsive single-image" v-bind:src="single_media.url"/>
                      </template>
                      <template v-else>
                       <video
                        id="video-player"
                        controls
                        class="single-image cld-video-player cld-video-player-skin-dark"
                        v-bind:data-cld-source="single_media.url"
                        >
                        </video> 
                      </template>
                    </div>
                  </template>
                  <template v-else>
                    <div class="carousel-item">
                      <template v-if="single_media.type == 'image'">
                        <img class="img-responsive single-image" v-bind:src="single_media.url"/>
                      </template>
                      <template v-else>
                        <video
                        id="video-player"
                        controls
                        class="single-image cld-video-player cld-video-player-skin-dark"
                        v-bind:data-cld-source="single_media.url"
                        >
                        </video>
                      </template>
                    </div>
                  </template>
                </template>
              </div>
              <!-- Left and right controls -->
              <a class="carobbusel-control-prev" href="#demo" data-slide="prev">
                <span class="carousel-control-prev-icon"></span>
              </a>
              <a class="carousel-control-next" href="#demo" data-slide="next"  v-on:click="initializePlayer()">
                <span class="carousel-control-next-icon"></span>
              </a>
            </div>  
          </div>
        </div>

      </div>
    </template>
In the template above, what we want to achieve is to display the media for the particular product. If you take a look at when we built the component, we make a request to the backend and then send the response to the Vue component.
We need to know if the media being displayed is an image or a video. So we check in the template;
    //..
    <template v-if="single_media.type == 'image'">
      <img class="img-responsive single-image" v-bind:src="single_media.url"/>
    </template>
    <template v-else>
     <video
      id="video-player"
      controls
      class="single-image cld-video-player cld-video-player-skin-dark"
      v-bind:data-cld-source="single_media.url"
      >
      </video> 
    </template>
    //..
If it has type of image, we display the image in the carousel but if type is a video, we use the Cloudinary VIdeo Player to display the video. To initialize the video player, we add v-on:click event to the > button. Once the button is clicked, the video player is initialized with the video.
PS: Cloudinary’s video player also playing videos by tags and playing playlists. You can read more about it here.
The preview view has some scoped styling as follows :
    <style scoped>
      h1, h2 {
        font-weight: normal;
      }
      ul {
        list-style-type: none;
        padding: 0;
      }
      li {
        display: inline-block;
        margin: 0 10px;
      }
      a {
        color: #42b983;
      }
      .carousel-inner{
        height : 500px;
      }
      .carousel-item{
        height : 100%;
      }
      .single-image{
        width : 100%;
        height: 100%;
        object-fit : fill;
      }
      #demo{
        margin-left: 30px;
        margin-right: 30px;
      }
    </style>
Building the Back-end
To build our back-end, we need to change directory to the root directory of our application and then install the node modules:
    cd preview-app 
    npm install cors express body-parser dotenv request connect-multiparty cloudinary
Once this is done, you have successfully installed all the modules necessary for you to build the project.
Create a server.js file
Now we need to create a file that will contain the instructions for our server to work In your video-suggestion directory,
    touch server.js
This will be the start-up file that will be referenced when your server is running In your server.js file, you need to
Import the node modules
    require('dotenv').config()
    const cors       = require('cors')
    const express    = require('express')
    const bodyParser = require('body-parser')
    const multipart  = require('connect-multiparty')
    const request    = require('request')
    const cloudinary = require('cloudinary')

    //...
Once you have imported your node modules, you can then use them freely all through your script.
Create your express app
Now we create our express app instance by adding the following to the server.js
    //...

    const app = express()

    //...
Load the middlewares
We load the middlewares in our server.js by adding the following
    //...

    app.use(cors())
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    const multipartMiddleware = multipart();

    //...
Here, we set our app to use cors . We also instructed the app the parse the requests in JSON format.
Configure the Cloudinary Client
We need to configure our cloudinary client using your CLOUD_NAME, API_KEY and API_SECRET
    //...

    cloudinary.config({
        cloud_name: 'CLOUDINARY_CLOUD_NAME', 
        api_key: 'CLOUDINARY_API_KEY', 
        api_secret: 'CLOUDINARY_API_SECRET'
    });

    //...
Once this is done, then we have successfully configured our Cloudinary client.
Create app routes
Our back-end server is very simple, it’s an express web server with two major routes:
  • /products - Lists all the products available for sale.
  • /getProductInfo - Returns data for the selected product.
    //...
    app.get('/products', multipartMiddleware, function(req, res){
      return res.json([
        {id: '1', name: 'UltraLight Mechanical Keyboard'},
        {id: '121', name: 'IPhone X'},
        {id: '23', name: 'Tesla S'},
        {id: '42', name: 'Work Shoes'}
      ]);
    });

    app.get('/getProductInfo/:id', multipartMiddleware, function(req, res){
      console.log( req.params.id );
      return res.json({
        media:        [
          {
            id:       '0',
            type:     'image',
            url:      'https://static.pexels.com/photos/265631/pexels-photo-265631.jpeg'
          },
          [...]
          {
            id:       '3',
            type:     'video',
            url:      
                'http://res.cloudinary.com/og-tech/video/upload/s--ZWPqo282--/v1514966645/sea_turtle-short_z1pr4o.mp4'
          },
        ],
        product_name: 'Ultra Thin Mechanical Keyboard',
        product_desc: 'This keyboard gives you the clack to your click',
        product_price: '200'
      })
    });

    //...
In the above, we see the routes returning responses in JSON format for it to be further used at the frontend. You may have observed that a lot (all) the data returned to the user was static. In a real-world application, you would return dynamic data to the user.
The /productInfo route accepts the id of your product so that is what you would use to identify what data to serve instead of just returning static json data. In other words, you can make further query to your database or cloud storage to fetch the information and return the data in the format used above.
Configure Application Port
Now we set the port we want the app to listen on:
    [...]

    let port = 3128 || process.env.PORT;

    app.listen(port, function () {
      console.log('App listening on port ' + port + '!');
    });
Conclusion
In this article we have see how to leverage Cloudinary’s image and video capabilities using Vue.js to build an Amazon-like preview app for products.
You also can head over here if you want to find out more about optimizing images served on your website f and more about improving your store’s SEO by engineering better Google mobile search ranking through image optimization.
Now you can use concepts shared here in building your own application for the #NextBillionUsers
Here a link to the GitHub repository for more references.