Session 10: Vue.Js – II (Vuex)
1. Clone the following repository Repo.
2. Setup the project/App
>> npm install
3. Compiles and hot-reloads for development
>> npm run serve
If everything is ok, your app should be running on http://localhost:8080/
When you open that address in your browser, the Products page (View) should look like the following snapshot:
The app contains a view (Products.vue) that contains two components (ProductCompoOne.vue and ProductCompoTwo.vue).
Products.vue └── ProductCompoOne.vue └── ProductCompoTwo.vue
The Products.vue View contains a simple array of objects (productList) that is passed to both of ProductCompoOne.vue and ProductCompoTwo.vue as props.
If you followed the installation instructions in Practical session 8, you should have Vuex installed in your IDE (e.g., VS Code). To be sure it has been installed successfully, open your package.json file and you should see it installed in your dependencies.
Creating a central store for your App
Task1. Move the productList array from Products.vue to the state section within index.js
file in the store folder, i.e., delete it from Products.vue and add it to the index.js
in the state section:
// /store/index.js import { createStore } from 'vuex' export default createStore({ strict: true, state: { // productList should be placed here productList: [...] }, getters: { }, mutations: { }, actions: { } })
Task2. Remove the props: (props: ["productList"],) from ProductCompoOne.vue and ProductCompoTwo.vue since we do not need them anymore.
Task3. Remove the passed data (:productList="productList") from the components call in the Products.vue file since we also do not need to pass the data in this way anymore. The template section of the Products.vue should looks as follows
// Products.vue <template> <div class="products"> <h2> Our Products page</h2> <product-compo-one></product-compo-one> <product-compo-two"></product-compo-two> </div> </template> ...
Task4. Since we are not receiving the data as a prop anymore, we need to reach out to the store to get the data. This can be done by adding the following code in both ProductCompoOne.vue and ProductCompoTwo.vue
Note: you should remember how we define a computed property (productList()). $store.state.productList refers to the array you have defined in the state of the store.
// ProductCompoOne.vue // We are using ProductCompoOne in the script here, yet you should do the same to ProductCompoTwo. <script> export default { name: "ProductCompoOne", data: function() { return { }}, computed: { productList(){ return this.$store.state.productList } } } </script>
By now, your page should look exactly the same as it used to look before using Vuex.
Task5. We want to offer a 50% discount on the book prices in ProductCompoOne.vue. Therefore, we need to: (1) define a getter to do that job, and (2) change the data to be presented in ProductCompoOne.vue to the new data returned by the created getter, which requires (i) reaching out to the store to get the data (productListsale), and (ii) change the name of the array we use in the template section of the ProductCompoOne.vue component from productList to productListsale.
We can do that by modifying the code in /store/index.js
and ProductCompoOne.vue, as follows:
// /store/index.js import { createStore } from 'vuex' export default createStore({ strict: true, state: { productList: [...] }, getters: { // .map is a higher-order function that creates a new array populated with the results of calling a provided function on every element in the calling array. – Lecture 5 productListsale: state => { var productListsale = state.productList.map(product => { return { id: product.id, author: product.author, price: product.price / 2, book: product.book, goodreads: product.goodreads } }); return productListsale }, }, mutations: { }, actions: { } })
// ProductCompoOne.vue <template> ... <li class="item" v-for = "product in productListsale" :key="product.id"> ... </template> <script> export default { name: "ProductCompoOne", ... computed: { productListsale(){ return this.$store.getters.productListsale }, } } </script>
If everything is ok, the Products page (View) should look like the following snapshot:
Now, we will modify the “state” (data) in the store using mutations:
Task5.1. Uncomment the two button tags in the template of Products.vue.
// Products.vue <template> ... <button v-on:click="IncreasePrice "> Increase price </button> <button v-on:click="DecreasePrice"> Decrease price </button><br/> ... </template> ...
Task5.2. Each of these buttons is supposed to increase/decrease the price of each item in productList by 1 by calling IncreasePrice and DecreasePrice functions respectively. To do that, add the definitions of these two functions to the script section in Products.vue to commit the mutations in the store, as follows:
// Products.vue <script> ... methods: { IncreasePrice: function() { this.$store.commit("IncreasePrice") }, DecreasePrice: function() { this.$store.commit("DecreasePrice") } ... </script>
Task5.3. Now, we need to add the definitions of the created mutations to the mutation section in the store, as follows:
// /store/index.js import { createStore } from 'vuex' export default createStore({ strict: true, state: { productList: [...] }, getters: { productListsale: state => {..}, }, mutations: { //The .forEach() method executes a callback function on each of the elements in an array in order. – Lecture 5 IncreasePrice: state => { state.productList.forEach(product => { product.price += 1; }) }, DecreasePrice: state => { state.productList.forEach(product => { product.price -= 1; }) } }, actions: { } })
If everything is ok, the two buttons should be working as expected, and the Products page (View) should look like the following snapshot:
As described in the lecture, using mutations to directly commit changes to the state in the store is not advisable, and dispatching actions to commit such changes is the right way, we need to modify our code.
Task6.1. Modify the definitions of the IncreasePrice and DecreasePrice functions in the script section in Products.vue to dispatch actions that will commit the mutations in the store, as follows:
// Products.vue <script> ... methods: { IncreasePrice: function() { this.$store.dispatch("IncreasePriceAct") }, DecreasePrice: function() { this.$store.dispatch("DecreasePriceAct") } ... </script>
Task6.2. Now, we need to add the definitions of the created actions to the action section in the store, as follows:
// /store/index.js import { createStore } from 'vuex' export default createStore({ strict: true, state: { productList: [...] }, getters: { productListsale: state => {..}, }, mutations: { IncreasePrice: state => { ... }, DecreasePrice: state => { ... } }, actions: { IncreasePriceAct: act => { setTimeout(function() { act.commit("IncreasePrice") }, 1000) }, DecreasePriceAct: act => { setTimeout(function() { act.commit("DecreasePrice") }, 1000) } } })
Test your App, and if everything is working as expected, you have learned the core aspects of Vuex.
Note: The complete solution exists in the following repository (will be made available after the session): Repo.