Saturday, October 6, 2018

Vue.js Routing With vue-router

If your Vue.js 2 applications grows bigger and consists of multiple views routing becomes a core part of your project. Let’s explore how you can use the Vue core package vue-router to implement routing in your web application.

Setting Up Our Vue.js 2 Project

We’re using Vue CLI to set up our Vue.js project. If you haven’t installed Vue CLI on your system yet you need to complete the installation first by using the following command:

 $ npm install -g vue-cli

 Having executed the installation successfully a new command vue becomes available. You can use this command the initiate a new Vue project in the following way:

 $ vue init webpack vue-router-01

 You're being asked a few questions on the command line to complete the creation of the project. One question asks if you want to add vue-router to the project. Answer with Y (yes) to make sure the the Vue router package is added to the initial project setup.

 Next you need to change into the newly created project folder:

 $ cd vue-router-01

 and complete the installation of dependencies by executing the following command:

 $ npm install

 Now the development web server can be started by entering:

 $ npm run dev

Installing vue-router Package

If you’re configuring your project with Vue CLI like shown before the vue-router package is added to your project automatically.

 If you would like to add the vue-router package manually to your project later, you can do so by using the npm command in the following way:

 $ npm install vue-router —save

Add vue-router To Our Application

If you’ve followed the recent steps and added vue-router to the initial configuration by using Vue CLI, routing is activated by default. The activation is taking place in file src/router/index.js:
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/components/Hello'
Vue.use(Router)
export default new Router({
  routes: [
    {
      path: '/',
      name: 'Hello',
      component: Hello
    }
  ]
})
First Router is imported from the vue-router package via import statement. Next you need to call
Vue.use(Router)
to make sure that Router is added as a middleware to our Vue project.

Setting Up Routes

A default route configuration is already included in file src/router/index.js. The route configuration is a JavaScript object consisting of three properties path, name and component. The configuration object is added to an array. The array needs to be assigned to the routes property of the object which is passed to the constructor of Router.

 As you can see in the listing above the Router instance is created and exported. To activate the Router configuration the Router instance is imported in file src/main.js:

 import router from './router'

 The Router instance is then used to create Vue application instance, as you can see in the following:
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})

Standard Routes

Standard routes are easy to define. The default route configuration is a typical example of a standard route:
{
      path: '/',
      name: 'Hello',
      component: Hello
    }
The route path is defined by using the path property. Setting the value of that property to ‘/’ means that this is the default route of your application. If the application is running on localhost:8080 for example the default route is activated when the user accesses http://localhost:8080 in the browser.

Routes With Parameters

Let’s add a second route configuration object to the array:
export default new Router({
  routes: [
    {
      path: '/',
      name: 'Hello',
      component: Hello
    },
    {
      path: '/firstroute/:name',
      name: 'FirstRoute',
      component: FirstRoute
    }
  ]
})
For the new route configuration the path value ‘/firstroute/:name’ is used. The last part of the path string is defining a routing parameter: name. The routing parameter enables us to pass a value to the routes component via URL, e.g.:

 http://localhost:8080/#/firstroute/bob

 In this case the value of the name routing parameter is bob. The parameter can be accessed in the route component FirstRoute. Let’s create a new file src/components/FirstRoute.vue and insert the code:
<template>
   <div>
     <h1>{{ msg }}</h1>
       Hello {{ $route.params.name }}
   </div>
</template>
<script>
  export default {
    name: 'firstroute',
    data () {
      return {
      msg: 'FirstRoute'
    }
  }
}
</script>

Embedding Routes Output

If a certain route is accessed the assigned Vue component is called and rendering it’s HTML output based on the component’s template code. The HTML output of the route components needs to be part of the browser output. The following placeholder element is used to define the place in the HTML page where the route component output should be inserted:
<router-view></router-view>
In our Vue application this element is already placed in the template code of App component (in file src/App.vue), as you can see in the following:
<template>
   <div id="app">
     <img src="./assets/logo.png">
     <router-view></router-view>
   </div>
</template>
If you now try to access FirstRoute in the browser you should see the output of component FirstRoute.

Child Routes

It’s also possible to define child routes. Let’s take a look at the following extended router configuration:
export default new Router({
   routes: [
     {
       path: '/',
       name: 'Hello',
       component: Hello
     },
     {
       path: '/firstroute/:name',
       name: 'FirstRoute',
       component: FirstRoute,
       children: [
         {
           path: 'child',
           component: FirstRouteChild
         }
       ]
     }
   ]
})
The property children has been added to the configuration object of FirstRoute. The property gets assigned an array with is containing another routes configuration object for route FirstRouteChild. Let’s implement FirstRouteChild as well. Create a new file FirstRouteChild.vue in folder src/components and insert the following code:
<template>
  <div>
    <h1>{{ msg }}</h1>
  </div>
</template>
<script>
export default {
  name: 'firstroutechild',
  data () {
    return {
      msg: 'FirstRouteChild'
    }
  }
}
</script>

Linking To Routes

The user is able to access a route by entering the corresponding URL in the browser directly. If you would like to add links to route URLs you can make use of the <router-link> element to generate a HTML link automatically:
<router-link to="/route-one/route-one-child">Link to route one, child one</router-link>
The to attribute contains the path to the route. You can also use the a colon before the to attribute to be able to assign a dynamic expression:
<router-link :to="myRouteTargetString">Link to dynamic route</router-link>
To link to the three routes which are available in our application we’re changing the implementation of App component in file App.vue:
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <div>
      <ul>
        <li><router-link to="/">Home</router-link></li>
        <li><router-link to="/firstroute/Sebastian">FirstRoute</router-link></li>
        <li><router-link to="/firstroute/Sebastian/child">FirstRouteChild</router-link></li>
      </ul>
    </div>
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: 'app'
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
Now the final result should look like the following:


By clicking on the link FirstRoute the view changes to include the output of FirstRoute component without reloading the page:


A click on link FirstRouteChild changes the view to also contain the output of component FirstRouteChild in addition to the output of FirstRoute component:


This post has been published first on CodingTheSmartWay.com.

Vue.js 2 State Management With Vuex — Introduction

If your Vue.js application grows bigger and consists of multiple components you might run into the problem of how to share data across the those components and make sure that components which are using the same data are always updated if data is changing.

 However, if your application is small you can solve this problem by emitting events to inform other components about data changes. If you applications grows bigger keeping track if all those events becomes difficult and it’s hard to keep the overview of components which need to trigger events and components which need to listen to those events.

 To solve that problem you can use Vuex which makes it possible to use a centralized state management in your application. Vuex is a library which helps you to enforce a Flux-like application architecture in your Vue.js 2 application.

 So what exactly is meant by state and centralized state management? Simply, you can think of state as just data you use in your application. So a centralized state is just data you’re using by more than one component (application level state).

 Vuex introduces a centralized state management by implementing the following core concepts (Store concept):
  • State Tree: An object containing the data
  • Getters: Used to access data from the state tree of the store
  • Mutations: Handler functions that perform modifications of data in the state tree
  • Actions: Functions that commit mutations. The main difference to Mutations is that Actions can contain asynchronous operations
Don’t worry if this concept seems a little bit strange at first. We’ll explore the various building blocks in detail in the following. Take a look at this diagram form the Vuex docs:
This overview is great for understanding the flows of actions and data in the store concept. From the diagram you can see that the data flow is unidirectional. A Vue component initiates a state change by dispatching an Action. The Action method can be used to perform asynchronous operation (e.g. accessing a web service to retrieve data) and then commit a Mutation. The Mutation performs the modification of the state tree. A new state becomes available is used by components to display the changed data.

Want to dive deeper into Vue.js 2 and Vuex — Check out the great online course :
Vue.js 2 The Complete Guide (incl. Vuex).

Let’s see how everything works by building a sample application next.

Setting Up A Vue.js 2 Project And Installing Vuex

The easiest way to set up a new Vue.js 2 project is to use Vue CLI. To install Vue CLI on your system use the following command:

 $ npm install --global vue-cli

 Now we're ready to use the vue command to initiate a new Vue project:

 $ vue init webpack vuex-test-01

 Next, change into the newly created project folder:
$ cd vuex-test-01

 Use NPM to install the default dependencies
$ npm install

 Finally you can start up the development web server by using the npm command in the following way:
$ npm run dev

 In the next step we need to add the Vuex library to the project.

 Vuex is an official Vue package, but not part of the core library. If you want to use Vuex in your project, you first need to install it via NPM:

 $ npm install vuex --save

 This makes sure that this package is downloaded in the node_modules folder of the project and that this dependency is added to package.json file.

Creating A Store

Having installed Vuex, we’re now ready to create a first store in our application. Within the src folder create a new subfolder named store. Within that folder create a new file store.js and insert the following code:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
  count: 0
}
export default new Vuex.Store({
  state
})
First, we need to use two import statements to make Vue and Vuex available. Second, a call of

 Vue.use(Vuex)

 is needed to activate Vuex for our application.

 The initial state of the store is defined by the state object. To keep things simple we're only defining one property count and set the initial value to 0. The state object is used to create the store instance by the method Vuex.Store and passing in an object containing state.

 Now we're able to inject the store into our Vue application instance. Open file main.js and adapt the code as follows:
import Vue from 'vue'
import App from './App'
import store from './store/store'
Vue.config.productionTip = false
new Vue({
  el: '#app',
  store,
  template: '<App/>',
  components: { App }
})
First you need to import store. Next, add store to the configuration object which is passed to the Vue constructor. By providing the store option to the root instance, the store will be injected into all child components of the root and will be available on them as this.$store.

Accessing State

Using the $store Object

Now, that the $store object is available in all components we’re able to use the following expression in one of our component templates, e.g. in Hello.vue:

 {{ $store.state.count }}

 This will insert the value of count (0) in the HTML output which is generated and displayed

Using Getters

Now you’ve learnt how to create a store, define the state and access state properties by using the $store object. The next step is to access state by adding Getters to the store. You can compare Getters to Computed Properties which are available on component level. For example we can use a Getter to evaluate if the value of count is even or odd by adding the following code to store.js:
const getters = {
  evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}
To make sure that the defined Getter is part of the Store, we need to add this Getters to the store configuration object which is passed to the call of Vuex.Store at the end of the file:
export default new Vuex.Store({
  state,
  getters
})
To use the evenOrOdd Getter in our component we need to make the following change in our component implementation in file Hello.vue:
<script>
import { mapGetters } from 'vuex'
export default {
  name: 'hello',
  computed: mapGetters([
    'evenOrOdd'
  ])
}
</script>
Notice, that we’re adding the mapGetters function from the vuex package first. Then we’re calling mapGetters, passing in as a parameter a array listing the Getters we would like to use and assign the return value to the computed property of the component configuration object.

 Now we can make use of evenOrOdd by including it in the template like you can see in the following:

 Counter: {{ $store.state.count }} times, count is {{ evenOrOdd }}.

 If the value of count in the store is even it returns the string “even”. If the value is odd it returns the string “odd”.

Modifying State

synchronously By Using Mutations

The next step is to modify the state. To directly modify state properties Mutations are used. Mutations are passed the current state and an optional payload. Mutations must be synchronous. Let’s define two Mutations in file store.js to increment and decrement:
const mutations = {
  increment (state) {
    state.count++
  },
  decrement (state) {
    state.count--
  }
}
Again, add the mutations object to the object which is passed to Vuex.Store:
export default new Vuex.Store({
  state,
  getters,
  mutations
})

asynchronously By Using Actions

In many cases it’s not sufficient to modfiy the state with a Mutation synchronously. Instead you need to perform asynchonous operations to retrieve values from a backend (e.g. accesing a data base or a web service) and then updating the state with that new value. In that case we need to add Actions to our store, as you can see in the following code listing:
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const state = {
  count: 0
}
const mutations = {
  increment (state) {
    state.count++
  },
  decrement (state) {
    state.count--
  }
}
const actions = {
  increment: ({ commit }) => commit('increment'),
  decrement: ({ commit }) => commit('decrement'),
  incrementIfOdd ({ commit, state }) {
    if ((state.count + 1) % 2 === 0) {
      commit('increment')
    }
  },
  incrementAsync ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('increment')
        resolve()
      }, 1000)
    })
  }
}
const getters = {
  evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}
export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})
Here you can see that the actions object is containing four functions:
  • increment
  • decrement
  • incrementIfOdd
  • incrementAsync
Each Action handler function can receive two parameters, a context object giving us access to a commit method and the current state and a optional payload object.
actions: {
  increment (context, payload) {
    ...
  }
}
The second parameter payload is only need if you want to pass in further information, e.g. values used to set a new state. The context object gives you access to the commit method, so that you can commit Mutations like you can see in the following:
context.commit('increment')
In our example we’re using the ES6 object deconstruction and arrow function feature, so that the code can be rewritten to:
increment: ({ commit }) => commit('increment')
The incrementAsync Action function demonstrates how to execute asynchronous code as part of an Action function.

 Now let’s see how we can apply the four action methods in the corresponding template:
<template>
  <div class="container">
    <div class="row text-center">
        <h3>Clicked: {{ $store.state.count }} times, count is {{ evenOrOdd }}.</h3>
        <button class="btn btn-success" @click="increment">+</button>
        <button class="btn btn-danger" @click="decrement">-</button>
        <button class="btn" @click="incrementIfOdd">Increment if odd</button>
        <button class="btn" @click="incrementAsync">Increment async</button>
    </div>
  </div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
  name: 'hello',
  computed: mapGetters([
    'evenOrOdd'
  ]),
  methods: mapActions([
    'increment',
    'decrement',
    'incrementIfOdd',
    'incrementAsync'
  ])
}
</script>
Here you can see that first we’re extending the import statement to also import the mapActions function. The return value of the mapActions call is assigned to the methods property of the component configuration object. We need to pass an array to mapActions containing strings with the names of the Actions we would like to make available to the component.

 With this code in place we’re able to include four buttons in the template code and bind the click event of those buttons the the four Action functions. The result in the browser should now look like the following:
If you click on the plus and minus button you can immediately see that the output changes. The counter is updated and the output text contains the information if the new value is odd or even. If you click on button “Increment if odd” the counter is only incremented if the current counter value is odd. If you click on “Increment async” you’ll see that the counter is incremented with a delay of 1000 ms.

Summary

If your Vue application grows bigger and consists of multiple components which needs to share data, introducing a central state management might be the right approach for you project. By using the Vuex library it’s easy to implement central state management in your Vue app by introducing a central store in your application.

 With this tutorial you’ve gained a basic overview about the centralized state management concept and you’ve learned how to apply Vuex in your application. In one of the next tutorials we’ll use that knowledge and dive deeper into that topic by implementing a real-world Vue.js application with Vuex.
This post has been published first on CodingTheSmartWay.com.

vue-webpack-boilerplate

A full-featured Webpack setup with hot-reload, lint-on-save, unit testing & css extraction.
This template is Vue 2.0 compatible. For Vue 1.x use this command: vue init webpack#1.0 my-project

Vue-cli 3 is here, so this template is now considered deprecated.

This template was the main template for vue-cli verison 2.*.
Now that we have released a stable version of vue-cli 3, which incorporates all features that this template offers (and much more), we think that this template doesn't have any significant use for the future, so we won't put much resource in developing it further.
We will try and fix major issues should they arise, but not much more.
Feel free to fork this template if you want to keep it alive.

Documentation

  • For this template: common questions specific to this template are answered and each part is described in greater detail
  • For Vue 2.0: general information about how to work with Vue, not specific to this template

Usage

This is a project template for vue-cli. It is recommended to use npm 3+ for a more efficient dependency tree.
$ npm install -g vue-cli
$ vue init webpack my-project
$ cd my-project
$ npm install
$ npm run dev
This will scaffold the project using the master branch. If you wish to use the latest version of the webpack template, do the following instead:
$ vue init webpack#develop my-project
⚠️ The develop branch is not considered stable and can contain bugs or not build at all, so use at your own risk.
The development server will run on port 8080 by default. If that port is already in use on your machine, the next free port will be used.

What's Included

  • npm run dev: first-in-class development experience.
    • Webpack + vue-loader for single file Vue components.
    • State preserving hot-reload
    • State preserving compilation error overlay
    • Lint-on-save with ESLint
    • Source maps
  • npm run build: Production ready build.
    • JavaScript minified with UglifyJS v3.
    • HTML minified with html-minifier.
    • CSS across all components extracted into a single file and minified with cssnano.
    • Static assets compiled with version hashes for efficient long-term caching, and an auto-generated production index.html with proper URLs to these generated assets.
    • Use npm run build --reportto build with bundle size analytics.
  • npm run unit: Unit tests run in JSDOM with Jest, or in PhantomJS with Karma + Mocha + karma-webpack.
    • Supports ES2015+ in test files.
    • Easy mocking.
  • npm run e2e: End-to-end tests with Nightwatch.
    • Run tests in multiple browsers in parallel.
    • Works with one command out of the box:
      • Selenium and chromedriver dependencies automatically handled.
      • Automatically spawns the Selenium server.

Fork It And Make Your Own

You can fork this repo to create your own boilerplate, and use it with vue-cli:
vue init username/repo my-project

What is Vuex?

Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. It also integrates with Vue's official devtools extension
to provide advanced features such as zero-config time-travel debugging and state snapshot export / import.

What is a "State Management Pattern"?

Let's start with a simple Vue counter app:
new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})
It is a self-contained app with the following parts:
  • The state, which is the source of truth that drives our app;
  • The view, which is just a declarative mapping of the state;
  • The actions, which are the possible ways the state could change in reaction to user inputs from the view.
This is an extremely simple representation of the concept of "one-way data flow":
However, the simplicity quickly breaks down when we have multiple components that share common state:
  • Multiple views may depend on the same piece of state.
  • Actions from different views may need to mutate the same piece of state.
For problem one, passing props can be tedious for deeply nested components, and simply doesn't work for sibling components. For problem two, we often find ourselves resorting to solutions such as reaching for direct parent/child instance references or trying to mutate and synchronize multiple copies of the state via events. Both of these patterns are brittle and quickly lead to unmaintainable code.
So why don't we extract the shared state out of the components, and manage it in a global singleton? With this, our component tree becomes a big "view", and any component can access the state or trigger actions, no matter where they are in the tree!
In addition, by defining and separating the concepts involved in state management and enforcing certain rules, we also give our code more structure and maintainability.
This is the basic idea behind Vuex, inspired by Flux
, Redux and The Elm Architecture . Unlike the other patterns, Vuex is also a library implementation tailored specifically for Vue.js to take advantage of its granular reactivity system for efficient updates.
vuex

When Should I Use It?

Although Vuex helps us deal with shared state management, it also comes with the cost of more concepts and boilerplate. It's a trade-off between short term and long term productivity.
If you've never built a large-scale SPA and jump right into Vuex, it may feel verbose and daunting. That's perfectly normal - if your app is simple, you will most likely be fine without Vuex. A simple store pattern
may be all you need. But if you are building a medium-to-large-scale SPA, chances are you have run into situations that make you think about how to better handle state outside of your Vue components, and Vuex will be the natural next step for you. There's a good quote from Dan Abramov, the author of Redux:
Flux libraries are like glasses: you’ll know when you need them.

Installation

Direct Download / CDN

https://unpkg.com/vuex
Unpkg.com provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like https://unpkg.com/vuex@2.0.0.
Include vuex after Vue and it will install itself automatically:
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>

NPM

npm install vuex --save

Yarn

yarn add vuex
When used with a module system, you must explicitly install Vuex via Vue.use():
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
You don't need to do this when using global script tags.

Promise

Vuex requires Promise
. If your supporting browsers do not implement Promise (e.g. IE), you can use a polyfill library, such as es6-promise .
You can include it via CDN:
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>
Then window.Promise will be available automatically.
If you prefer using a package manager such as NPM or Yarn, install it with the following commands:
npm install es6-promise --save # NPM
yarn add es6-promise # Yarn
Furthermore, add the below line into anywhere in your code before using Vuex:
import 'es6-promise/auto'

Dev Build

You will have to clone directly from GitHub and build vuex yourself if you want to use the latest dev build.
git clone https://github.com/vuejs/vuex.git node_modules/vuex
cd node_modules/vuex
npm install
npm run build

 

Getting Started

At the center of every Vuex application is the store. A "store" is basically a container that holds your application state. There are two things that make a Vuex store different from a plain global object:
  1. Vuex stores are reactive. When Vue components retrieve state from it, they will reactively and efficiently update if the store's state changes.
  2. You cannot directly mutate the store's state. The only way to change a store's state is by explicitly committing mutations. This ensures every state change leaves a track-able record, and enables tooling that helps us better understand our applications.

The Simplest Store

NOTE: We will be using ES2015 syntax for code examples for the rest of the docs. If you haven't picked it up, you should
!
After installing Vuex, let's create a store. It is pretty straightforward - just provide an initial state object, and some mutations:
// Make sure to call Vue.use(Vuex) first if using a module system

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
Now, you can access the state object as store.state, and trigger a state change with the store.commit method:
store.commit('increment')

console.log(store.state.count) // -> 1
Again, the reason we are committing a mutation instead of changing store.state.count directly, is because we want to explicitly track it. This simple convention makes your intention more explicit, so that you can reason about state changes in your app better when reading the code. In addition, this gives us the opportunity to implement tools that can log every mutation, take state snapshots, or even perform time travel debugging.
Using store state in a component simply involves returning the state within a computed property, because the store state is reactive. Triggering changes simply means committing mutations in component methods.
Here's an example of the most basic Vuex counter app
.
Next, we will discuss each core concept in much finer details, starting with State.

 


Friday, October 5, 2018

Build an Online Shop with Vue scotch.io

Vấn đề hiện tại

Component là các hàm.
Lập trình là chia nhỏ vd thành các bài toán nhỏ => 
truyền dữ liệu giữa component và môi trường của nó
vue sử dụng props truyền dữ liệu từ cha đến con
$emit để gửi event từ con đến cha
Vấn đề:
khi có 3 cấp trở nên (ông
ông => props => cha => props => con => props => cháu chắt
cháu chắt => $emit => con => $emit => cha => $emit =>ông

Quản lý trạng thái
Vuex là thư viện giúp quản lý trạng thái của các component tập trung
Thay vì tìm  state trong mỗi component, bây giờ tập trung chúng vào 1 đối tượng state lớn