Node.JS Tutorial: REST API & CRUD Operations

Node.JS Tutorial: REST API & CRUD Operations

The following Tutorial will guide you through creating a simple REST API and CRUD Operations. Our Stack is ExpressJS, Mongoose and MongoDB.

This API will perform CRUD Operations on Data stored in MongoDB. It’s best to start from scratch and try creating this app by yourself. After you tried creating the App with the help of this tutorial, you can go ahead and clone the working Github Repository here. https://github.com/AndreyBulezyuk/nodejsrestapi


Before you continue with this tutorial, please consider signing up for our Mailing List. You’ll get awesome free tutorials, podcasts, free and paid courses into your inbox.


What is REST API and CRUD Operations

REST stands for representation state transfer (wiki) and stands for a software architectural style with a set of constraints which lead to REST Service. Our service (our ExpressJS Server) is an Adapter to another backend service or database. Therefore we call it API.

CRUD is much simpler. The letters stand respectively for Create, Read, Update and Delete. Thus, CRUD stand for a set of operations that allows us to manipulate our states or data (through our REST API).

NPM – Creating the Project

Creating the project is the first step. Create a folder and inside that folder type

npm create

Fill out the following promts that’ll appear after npm create. Then we need to install following packages for our project:

npm install --save express@4.17.1
npm install -g nodemon@1.19.4
npm install --save mongoose@5.7.11

Once there, you should have a file ‘package.json’ in your folder that look like the code below. Note that i added “dev” to “scripts”, where we call nodemon. Nodemon allows us to restart the server automatically as soon as we update a file in our project.

{
  "name": "nodejsboostrap",
  "version": "1.0.0",
  "description": "Basic NodeJS Excercise for German IT Academy (git-academy.com)",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "keywords": [
    "nodejs",
    "germanitacademy",
    "gitacademy",
    "expressjs",
    "nodemon"
  ],
  "author": "Andrey Bulezyuk @ German IT Academy",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "mongoose": "^5.7.11"
  }
}

In schema/ i added a new schema product.js which is pretty similar to the one you will find in the official docs. This is the Object Schema that we will be working with.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var productSchema = new Schema({
  title:  String,
  author: String,
  body:   String,
  price: Number,
  comments: [{ body: String, date: Date }],
  date: { type: Date, default: Date.now }
});

var Product = mongoose.model('products', productSchema);
module.exports = Product

If you want to learn how to build VueJS Applications that can read from a REST API, take a look at our new VueJS Certification.

Express.JS – REST API Routes

Our Express.js backbone is pretty tiny, only 8 lines of code. We tell Express to use a bodyParser, so we can access the body as a JSON Object. We tell ExpressJS to use our product route and then we start the server.

const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const product = require('./routes/product')

app.get('/', (req, res) => res.send('Hello from German IT Academy!'))
app.use(bodyParser.json())
app.use('/product', product)
app.listen(3000, () => console.log(`Example app listening on port 3000!`))

Just a quick test whether the server is up and ready!

C:\Users\andre>curl localhost:3000
Hello from German IT Academy!

Looks good. Now. Our product route is where all the magic actually happens. All our Documents in MongoDB will be created, read, updated and deleted from this place: routes/product.js.

Now let’s got through each Route (GET, POST, PATCH, DELETE) and see what mongoose method we used to make this work. Note, this is not the ultimate way to do it. Feel free to try out different Methods provided by Mongoose.

The first Route is a GET Route. Which means it only return the Entries in the Database by executing:

router.get('/', async function (req, res) {
    const products = await product.find().exec()
    res.send(products)
})
REST API Request with User Interface for REST API
A simple GET Request, to test whether our API returns the entries from MongoDB.

The POST Route insert new Entries into the MongoDB Database. It uses the data from the Request payload in JSON Format. It’s important to notice that the body payload has to have the right set of Attributes. Meaning that the Request initiator must know what Attributes a Product can/must have.

router.post('/', function (req, res) {
    product.create(req.body)
    res.send('POST Request accepted.')
})

The Patch Route is a bit more complicated. It is cause by the MongoDB Syntax of updating a Document. If we want to update only some Attributes/Keys of a MongoDB Document, we must use the key “$set: {}”. The req.body contains the updated values of attributes/keys for the specified Document. We specify what document to update via the URL Parameter id. Finally we’ll search through MongoDB for an Entry with the corresponding MongoDB “_id”!

router.patch('/:id', async function (req, res) {
    const result = await product.updateMany({
        "_id": String(req.params.id)
    }, { $set: req.body })
    res.send('PATCH Request accepted.')
})

Last and but not least, the Deletion of a Document. The Delete Route. We specify the “_id” of the DOcument in the URL and invoke the Mongoose DeleteMany() Method.

router.delete('/:id', async function (req, res) {
    const result = await product.deleteMany({
        "_id": String(req.params.id)
    })
    res.send('DELETE Request accepted.')
})

That is it, we are done, but, you probably want to see the whole code? Here you go:

var express = require('express')
var router = express.Router()
const mongoose = require('mongoose')
const product = require('../schema/product')
mongoose.connect('mongodb://localhost/nodejsrestapiboostrap', {
    useNewUrlParser: true,
    useUnifiedTopology: true 
});


router.get('/', async function (req, res) {
    const products = await product.find().exec()
    res.send(products)
})

router.post('/', function (req, res) {
    product.create(req.body)
    res.send('POST Request accepted.')
})

router.patch('/:id', async function (req, res) {
    const result = await product.updateMany({
        "_id": String(req.params.id)
    }, { $set: req.body })
    res.send('PATCH Request accepted.')
})

router.delete('/:id', async function (req, res) {
    const result = await product.deleteMany({
        "_id": String(req.params.id)
    })
    res.send('DELETE Request accepted.')
})

module.exports = router
VueJS Tutorial – #6 Template Syntax

VueJS Tutorial – #6 Template Syntax

Before we start with another VueJS Tutorial, let me promote our VueJS Course with a Certificate right at the beginning. You’ll see more interactive and deeper Content about VueJS. At the end you’ll also receive a VueJS Certificate from German IT Academy. That’s it.

Problem / Task

While coding a solution for the following tasks, make sure to make use of every single v-directive at least once: v-text, v-html, v-once and v-model. Also, do not forget to use conditional and iterative v-directives, if needed.

  • Create a new Vue Component CustomizeDonut.vue
  • Use following JSON Sample data as ‘donuts‘ data property
  • In CustomizeDonut, create 4-step input process, in which a user has to perform one data-selection in each step. Use v-model as much as you can. The steps should have individual h1 titles and individual Button Values.
    • Select Donut by name (see Sample Data)
    • Select Batter Type
    • Select Topping Type
    • Last step: Show the summary and a dummy button “Order Donut”

Hint

Here is a sample Object of a Donut. Few things stand out: We have a type and a name. We have a list of “batters” and “topping”, which contain object with further details.

	{
		"id": "0001",
		"type": "donut",
		"name": "Cake",
		"ppu": 0.55,
		"batters":
			{
				"batter":
					[
						{ "id": "1001", "type": "Regular" },
						{ "id": "1002", "type": "Chocolate" },
						{ "id": "1003", "type": "Blueberry" },
						{ "id": "1004", "type": "Devil's Food" }
					]
			},
		"topping":
			[
				{ "id": "5001", "type": "None" },
				{ "id": "5002", "type": "Glazed" },
				{ "id": "5005", "type": "Sugar" },
				{ "id": "5007", "type": "Powdered Sugar" },
				{ "id": "5006", "type": "Chocolate with Sprinkles" },
				{ "id": "5003", "type": "Chocolate" },
				{ "id": "5004", "type": "Maple" }
			]
	}
  • To have a multi-step process, you have to track the status of the current process. You can do it with an object. Or simpler yet, a variable that will store the step number that the user is currently performing.

Solution

Here is the solution we came up with for this VueJS Tutorial. Please that is no definite solution, except that the v-directive are used correctly. We smashed the solution into one Vue Component, that way we make it simpler to understand by not having the code in 3-4 different files.

<template>
  <div id="CustomizeDonut">
    <h1 v-text="stepConfig[step].title"></h1>
      <div v-if="step == 1">
            <select v-model="userInput.donutId">
                <option v-for="donut in donuts" :value="donut.id" :key="donut.id" v-text="donut.name"/>
            </select>
      </div>
      <div v-else-if="step == 2">
            <select v-model="userInput.batterId">
                <option v-for="batter in batters" :value="batter.id" :key="batter.id" v-text="batter.type"/>
            </select>
      </div>
      <div v-else-if="step == 3">
            <select v-model="userInput.toppingId">
                <option v-for="topping in toppings" :value="topping.id" :key="topping.id" v-text="topping.type"/>
            </select>
      </div>
      <div v-else-if="step == 4">
            Donut Id: {{userInput.donutId}}<br>
            Batter Id: {{userInput.batterId}}<br>
            Topping Id: {{userInput.toppingId}}<br>
      </div>
      <button @click="stepConfig[step].nextStep" v-text="stepConfig[step].button"/>
  </div>
</template>

<script>
    export default {
        name: "CustomizeDonut",
        data() {
            return {
                donuts: [{
                        "id": "0001",
                        "type": "donut",
                        "name": "Cake",
                        "ppu": 0.55,
                        "batters": {
                            "batter": [{
                                    "id": "1001",
                                    "type": "Regular"
                                },
                                {
                                    "id": "1002",
                                    "type": "Chocolate"
                                },
                                {
                                    "id": "1003",
                                    "type": "Blueberry"
                                },
                                {
                                    "id": "1004",
                                    "type": "Devil's Food"
                                }
                            ]
                        },
                        "topping": [{
                                "id": "5001",
                                "type": "None"
                            },
                            {
                                "id": "5002",
                                "type": "Glazed"
                            },
                            {
                                "id": "5005",
                                "type": "Sugar"
                            },
                            {
                                "id": "5007",
                                "type": "Powdered Sugar"
                            },
                            {
                                "id": "5006",
                                "type": "Chocolate with Sprinkles"
                            },
                            {
                                "id": "5003",
                                "type": "Chocolate"
                            },
                            {
                                "id": "5004",
                                "type": "Maple"
                            }
                        ]
                    },
                    {
                        "id": "0002",
                        "type": "donut",
                        "name": "Raised",
                        "ppu": 0.55,
                        "batters": {
                            "batter": [{
                                "id": "1001",
                                "type": "Regular"
                            }]
                        },
                        "topping": [{
                                "id": "5001",
                                "type": "None"
                            },
                            {
                                "id": "5002",
                                "type": "Glazed"
                            },
                            {
                                "id": "5005",
                                "type": "Sugar"
                            },
                            {
                                "id": "5003",
                                "type": "Chocolate"
                            },
                            {
                                "id": "5004",
                                "type": "Maple"
                            }
                        ]
                    },
                    {
                        "id": "0003",
                        "type": "donut",
                        "name": "Old Fashioned",
                        "ppu": 0.55,
                        "batters": {
                            "batter": [{
                                    "id": "1001",
                                    "type": "Regular"
                                },
                                {
                                    "id": "1002",
                                    "type": "Chocolate"
                                }
                            ]
                        },
                        "topping": [{
                                "id": "5001",
                                "type": "None"
                            },
                            {
                                "id": "5002",
                                "type": "Glazed"
                            },
                            {
                                "id": "5003",
                                "type": "Chocolate"
                            },
                            {
                                "id": "5004",
                                "type": "Maple"
                            }
                        ]
                    }
                ],
                step: 1, // We start with step 1
                stepConfig: {
                    1: {
                        title: "Donut Type",
                        button: `Go to Step 2`,
                        nextStep: () => {this.step = 2;}
                    },
                    2: {
                        title: "Batter Type",
                        button: `Go to Step 3`,
                        nextStep: () => {this.step = 3;}
                    },
                    3: {
                        title: "Topping Type",
                        button: `Final Step`,
                        nextStep: () => {this.step = 4;}
                    },
                    4: {
                        title: "Your Donut",
                        button: `Send order and Start over`,
                        nextStep: () => {this.step = 1;}
                    },
                },
                userInput: {
                    donutId: 0,
                    batterId: 0,
                    toppingId: 0
                }
            }
        },
        computed: {
            batters: {
                get() {
                    const currentDonut = this.donuts.filter(item => item.id == this.userInput.donutId)
                    return currentDonut[0].batters.batter
                }
            },
            toppings: {
                get() {
                    const currentDonut = this.donuts.filter(item => item.id == this.userInput.donutId)
                    return currentDonut[0].topping
                }
            }
        }
    }
</script>

<style>
button, option, select {
    margin: 20px 20px 20px 20px;
    font-size: 30px;
    color: rgb(255, 255, 255);
    background: rgba(255, 106, 0, 0.972)
}
</style>

Here is quite a lot happening, let’s go trough it.

First we have created a very simple step process by having a step data property and showing conditional HTML content based on that property; we achieve this with v-if and v-else-if v-directives.

Our data property stepConfig tells our App what title, what button text and what the button-action should be. Notice that we use a function for stepConfig.nextStep; this is because we use the v-directive v-on on our button. This directive needs a function as a parameter. @click=”stepConfig[step].nextStep”

Our computed properties help us extract or filter the correct batters and topping for the donut selected by the user.

In order to store user Input we created an object userInput and we mapped the keys “userInput.donutId”, “userInput.batterId” and “userInput.toppingId” with v-model to the select.options HTML Tags. This way our App has always the most recent user input saved in this Object.

If you want to learn more about VueJS and become a VueJS Certified Developer, please take a look at our VueJS Course + Certificate.

Here is a visual version of our solution. Please note, we know VueJS, but we suck at Design.

VueJS Tutorial- #5 Component Relationships

VueJS Tutorial- #5 Component Relationships

Welcome to another VueJS Tutorial. Let’s explore a very important topic in VueJS: the relationship between your Vue-Components. It’s crucial because everything is importing everything from everywhere anytime…or something like this.

For those who are new to VueJS Tutorial / VueJS Practice: we create a fictional problem / task for you to solve. To go through it and try and code a solution that would solve the issue. Then you compare it with the solution provided by German IT Academy.

Problem / Task

We need

  • create DataSourceList.vue that reads from state ‘datasources’ in vuex store.
  • For every Entry in the ‘datasources’ list, embed a a DataSourceItem.vue in the DataSourceList.vue.
  • DataSourceItem Component receives the props ‘id’ from the Parent. With that id the Child fetches all the data from ‘datasources’ in vuex store and displays them.
  • DataSourceItem Component receives a slot from the Parent and displays the content in a random place.

Hint

  • When importing a Vue-Component, do not forget to also import it inside the Parent Instance
  • As reminder, props are those data values that you pass to a child as a HTML Tag Attribute (:propName=data) and slot is the data between the HTML Opening and Closing tag.
  • To create Vuex Getter that receives a parameter from the outside, read: https://vuex.vuejs.org/guide/getters.html

You can learn more about VueJS with our VueJS Course and get VueJS certified.

Solution

Source code:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    datasources: [
      {
        id:0,
        name:'Google Cloud Storage',
        interface: 'REST API',
        activated: true
      },
      {
        id:1,
        name:'AWS S3 Bucket',
        interface: 'REST API',
        activated: true
      },
      {
        id:2,
        name:'On-Deman Server Storage',
        interface: 'SSH',
        activated: false
      }
    ]
  },
  getters: {
    getAllDataSources(state) {
      return state.datasources
    },
    getDataSourceById: (state) => (id) => {
      for (let i = 0; i < state.datasources.length; i++) {
        if (state.datasources[i].id == id) {
          return state.datasources[i]
        }
      }
    }
  },
  mutations: {
  },
  actions: {
  }
})

We have the list as a state variable. We have to getters: one for every DataSource and one getter that will return only the Entry with the specified id.

<template>
  <div class="DataSourceItem">
    <h2 :style="activated()"> 
      {{this.datasource.name}}
    </h2>
    <h3> {{datasource.interface}}</h3>
  </div>
</template>

<script>
export default {
  name: 'DataSourceItem',
  props: {
    id: Number
  },
  computed: {
    datasource: {
      get(){
        return this.$store.getters.getDataSourceById(this.id)
      }
    }
  },
  methods: {
    activated() {
      if (this.datasource.activated) {
        return 'background-color:green;'
      }
      return 'background-color:red;'
    }
  }
}
</script>

<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

The DataSourceItem.vue receives the id as a props. With that id the Component will retrieve the DataSource from Vuex Store. It’ll use the Vuex getter that we’ll define below.

<template>
  <div class="home">
    <img 
      alt="German IT Academy"
      width="200"
      src="https://i0.wp.com/code.git-academy.com/wp-content/uploads/2019/10/400dpiLogo.jpg?zoom=1.100000023841858&fit=2077%2C1175&ssl=1">
    <div v-for="datasource in datasources" :key="datasource.id">  
      <DataSourceItem :id="datasource.id"/>
    </div>
  </div>
</template>

<script>
// @ is an alias to /src
import DataSourceItem from '@/components/DataSourceItem.vue'

export default {
  name: 'DataSourceList',
  components: {
    DataSourceItem
  },
  computed: {
    datasources: {
      get(){
        return this.$store.getters['getAllDataSources']
      }
    },
  }
}
</script>

German IT Academy hopes you learned something new with this VueJS Tutorial. You can learn more about VueJS with our VueJS Course and get VueJS certified.

The visual version of the solution looks like this.

VueJS Course and VueJS Certificate
VueJS Practice – #4 Vuex Store

VueJS Practice – #4 Vuex Store

Here we go again, another VueJS Practice. This time we’ll discuss a very important part of every VueJS Application – the Central Data Store or Vuex Store. Vuex is a VueJS Plugin and extends your Application with centrally available Data and Methods.

Another VueJS Tutorail and Practice by German IT Academy.

Problem / Task

We want to create a Part that is centrally available in our Application: A Shopping Cart. Generate three products in the Vuex Store as states. Create a ProductList.vue View and a ShoppingCart.vue Component. The ProductList.vue should display the products stored in vuex – include a button “Add to Cart”. “Add to Cart” should add the selected Product to a centrally available list, so that the cart list can be access from any other Component. In that view, import ShoppingCart.vue, which should display all Items in the Cart.

Hint

  • For the products: use an Array or Object in Vuex.
  • Displaying multiple UI elements can be done with v-for directive.
  • Centrally available list is nothing else than a list of centrally avaiable products.
  • In order to follow the best practices, consider using
    • Vuex Getters to retrieve data
    • Vuex Actions to modify Vuex States from the outside (e.g. VueComponent)

Solution

As always, let’s jump right into the code.

<template>
  <div class="ShoppingCart">
    <h3>Shopping Cart</h3>
      <p v-for="(item, key) in cart" :key="key">
        {{ products[item].name }}
      </p>
      <hr>
  </div>
</template>

<script>
export default {
  name: 'ShoppingCart',
  computed: {
    products: {
      get() {
        return this.$store.getters['allProducts']
      }
    },
    cart: {
      get() {
        return this.$store.getters['getCartItems']
      }
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
<template>
  <div class="home">
    <img alt="Vue logo" width="300" src="https://i0.wp.com/code.git-academy.com/wp-content/uploads/2019/10/400dpiLogo.jpg?zoom=1.100000023841858&fit=2077%2C1175&ssl=1">
    <hr>
    <ShoppingCart/>
    <ul>
      <li v-for="product in products" :key="product.id">
        <h1> {{product.name}}</h1>
        <h3> {{product.price}} $</h3>
        <button @click="addToCart(product.id)">Add to Cart</button>
      </li>
    </ul>
  </div>
</template>

<script>
// @ is an alias to /src
import ShoppingCart from '@/components/ShoppingCart.vue'

export default {
  name: 'home',
  components: {
    ShoppingCart
  },
  methods:{
    addToCart(id) {
      this.$store.dispatch('addToCart', id)
    }
  },
  computed: {
    products: {
      get() {
        return this.$store.getters['allProducts']
      }
    }
  }
}
</script>

<style scoped>
li { 
  list-style-type: none;
  margin-bottom: 100px;
}
button {
  background-color: blue;
  color: white;
  font-size: 20px;
  border-radius: 25px;
  border: 1px solid black ;
}
</style>
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    products: {
      0: {
        id: 0,
        name: 'VueJS T-Shirt black',
        price: 39
      },
      1: {
        id: 1,
        name: 'VueJS T-Shirt white',
        price: 39
      },
      2: {
        id: 2,
        name: 'VueJS T-Shirt blue',
        price: 39
      }
    },
    cart: []
  },
  getters: {
    allProducts(state) {
      return state.products
    },
    getCartItems(state) {
      return state.cart
    }
  },
  mutations: {
    pushCart(state, payload){
      state.cart.push(payload)
    }
  },
  actions: {
    addToCart(context, payload) {
      context.commit('pushCart', payload)
    }
  }
})

Below you can see the visual result of the above code. If you are interested in more VueJS Practice, examples and tutorials, feel free to enroll in our VueJS Course and get certified by German IT Academy.

VueJS Practice

In the store.js we specify out getters. Our products and cart are just lists in the vuex states object. When we press on “Add to Cart”, we initiate a Vuex Action called “Add to Cart” with “this.$store.dispatch(‘addToCart’, id)“. The ShoppingCart.vue simply iterates over the Vuex State Cart and prints out the items.

It’d be better to outsource the v-for loop in ProductList.vue into another Vue-Component ProductItem.vue. ProductList.vue would import ProductItem.vue as a child, and pass a product id. Then, ProductItem.vue could display the product by selecting it via id.

VueJS Practice #3 – Instance Management

VueJS Practice #3 – Instance Management

Wecome to the third VueJS Practice from German IT Academy. In this module we are going to make sure that we understand the properties and the life cycle of a Vue-Component (or instance).

Problem

Create a Component ProductDetail.vue with

  • a product object with random values for keys (id, price, name)
  • a text input-field “quantity” with default value “0”
  • a text output calculating the total Costs (product price * quantity)

The Component must have following behavior:

  • Before the Component has been mounted, a text output “Specify Quantity.” appears .
  • Data property “quantity” is automatically updated with the input-field quantity.
  • After Quantity has been updated, a text output “Quantity updated.” appears.

Hint

  • For the text above all Content: think about a data property that will be changed on certain Component life cycles.
  • Regarding the quantity and input-field: Do you remember what v-model is good for?
  • For the behavior part: the words “before” and “after” should be ringing a bell in your head. Take a look at the life cycle diagram of VueJS Components/Instances.

Solution

Let’s jump right into the code. Read it and see if you understand everything. If not, see the explanation after the code.

<template>
  <div class="ProductDetail">
      <h2>{{notification}}</h2>
      <p>
        Quantity: <input type="text" v-model="quantity"><br>
        Total Value: {{ totalValue }}$
      </p>
  </div>
</template>

<script>
export default {
  name: 'ProductDetail',

  data() {
    return {
      product: {
        id: 1,
        name: 'VueJS Certificate',
        price: 999
      },
      quantity: 0,
      // You could set 'notification' to desired default value 
      // instead doing it trough life cycle hook 
      notification: '' 
    }
  },

  computed: {
    totalValue: {
      get() {
        return this.quantity*this.product.price
      }
    }
  },

  beforeMount() {
    if(this.quantity==0) {
      this.notification = 'Please specify quantity!'
    }
  },

  updated() {
    if(this.quantity != 0) {
      this.notification = 'Quantity updated!'
    }
  }
}
</script>

<style scoped>
h2 {
  color:orange;
  border: 1px solid orange;
  width:50%;
  margin: 0 25% 0 25%;
}
input {
  height: 40px;
  width: 70px;
  font-size: 25px;
}
.ProductDetail {
  text-align: center;
}

.ProductDetail p {
  font-size: 30px;
}
</style>

Starting with unimportant: styles are just css styles. The {{notification}} in <template> can be either “Please specify quantity” or “Quantity updated!”. The value of this data property changes with the life cycle hooks beforeMount() and updated(). The computed totalValue is an automatic way recalculate a value as soon as a data property (in our case quantity) changes. And then there are the data properties like products with dummy data. The input-field is mapped with quantity data property thanks to v-model.

This is how our result looks like. Simplistic and solves the Task/Problem at hand.

We hope you liked our VueJS Practice and learned something new. If you want to get a deeper insight into how VueJS works, consider enrolling in our VueJS Certificate Junior Course.

VueJS Practice – #2 Routing

VueJS Practice – #2 Routing

Learning by doing – VueJS Practice part 2. Let’s take a look at how a possible school-like question on your VueJS Test would look like. And how you would go about solving it. Btw if you want to dive deep into VueJS 2, take a look at our VueJS Certificate Course.

Problem

You need to create routes for the Vue-Components that we created in the previous VueJS Practice. Create a vue-route (use Vue-Router) for each of the following Components:

  • AssetsDetail.vue and AssetsList.vue
  • ProductsDetail.vue and ProductsList.vue
  • UsersDetail.vue and UsersList.vue

Routes for “*Detail.vue” Components must have a url parameter “id”. Router for “*List.vue” Components must have a url parameter “page”. Also, create a Vue Component called “navigation/TopMenu.vue” where you generate a list of router-links to the previously created routes; use a programmatic approach.

Hint

  • Vue-Router is the obvious choice for a simple routing task like this.
  • Regarding URL Parameters you cheat by reading about Dynamic Routing at Vue-Router Official Docs.

VueJS Practice – Solution

To define your router with Vue-Router, you need to write your routings to router.js. Here is how our prototype looks like.

import AssetsDetail from './components/assets/AssetsDetail.vue'
import AssetsList from './components/assets/AssetsList.vue'
import UsersDetail from './components/assets/UsersDetail.vue'
import UsersList from './components/assets/UsersList.vue'
import ProductsDetail from './components/assets/ProductsDetail.vue'
import ProductsList from './components/assets/ProductsList.vue'


export default new VueRouter({
    mode: 'history',
    routes: [{
            path: '/asset/:id',
            name: 'AssetsDetail',
            component: AssetsDetail
        },{
            path: '/asset/list/:page',
            name: 'AssetsList',
            component: AssetsList
        },{
            path: '/user/:id',
            name: 'UsersDetail',
            component: UsersDetail
        },{
            path: '/user/list/:page',
            name: 'UsersList',
            component: UsersList
        },{
            path: '/product/:id',
            name: 'ProductsDetail',
            component: ProductsDetail
        },{
            path: '/product/list/:page',
            name: 'ProductsList',
            component: ProductsList
        }
    ]
})

The TopMenu.vue Component has two benefits. First, it helps you debug your router.js while you are creating your routes configuration. Second, you can import the TopMenu to the most Upper VueComponent. This ways, if a route changes, your will have adjust the TopMenu.vue only, and the whole App will be updated. This is what i came up with for the Menu.

<template>
  <div id="menu">
    <ul>
      <li v-for="(key, item) in menu" :key="key">
        <router-link :to="item.url">{{ item.name }}</router-link>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "TopMenu",
  data() {
    return {
      menu: [
        {name: "List of Assets", url: "/asset/list/1"},
        {name: "First Asset", url: "/asset/1"},
        {name: "List of Users", url: "/user/list/1"},
        {name: "First User", url: "/user/1"},
        {name: "List of Products", url: "/product/list/1"},
        {name: "First Product", url: "/product/1"},
      ]
    }
  }
}
</script>

That’s it for our VueJS Practice. If you want more, take a look at our more detailed VueJS Certificate Course.