go to part...
So, to start let's create a new Laravel project.
laravel new cart
cd cart
Open the project in your favorite IDE, then open the package.json
file. We'll remove the dependencies we won't be using and add new ones. That's what the devDependencies
should look like:
"devDependencies": {
"axios": "^0.18",
"bulma": "^0.7.1",
"cross-env": "^5.1",
"laravel-mix": "^2.0",
"vue": "^2.5.7",
"vuex": "^3.0.1"
}
Save the file and run npm install
to install all the dependencies. Note that I replaced bootstrap
with bulma
- this is just a personal preference of CSS framework and is not the key thing in this tutorial. Now let's fix the assets files. First the sass files. Go to resources/assets/sass
folder and delete the _variables.scss
file. Then open app.scss
and replace its contents with this:
@import '~bulma/bulma';
And now to the resources/assets/js
folder. Edit bootstrap.js
file like this:
import axios from 'axios';
import Vue from 'vue';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
window.Vue = Vue;
And then the app.js
file like this:
require('./bootstrap');
Vue.component('example-component', require('./components/ExampleComponent.vue'));
const app = new Vue({
el: '#app'
});
As you can see we only kept Vue
and axios
inside those files as we've removed the rest of the dependencies from package.json
.
Finally, let's edit the resources/views/welcome.blade.php
view. This is going to be our main and only view for this lesson. It'll just have some standard bulma stuff to lay things out.
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Voerro Tutorial - Shopping Cart w/ Vue.js 2 & Vuex</title>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
<div id="app">
<nav class="navbar is-primary">
<div class="navbar-brand">
<a class="navbar-item" href="/">
Voerro Shopping Cart Tutorial
</a>
<div class="navbar-burger burger">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div id="navbarExampleTransparentExample" class="navbar-menu">
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="">
Cart (0)
</a>
<div class="navbar-dropdown is-boxed is-right">
<a class="navbar-item" href="">
Cart is empty
</a>
<hr class="navbar-divider">
<a class="navbar-item" href="">
Checkout
</a>
</div>
</div>
</div>
</div>
</nav>
<div class="section content">
<h1>Our Products</h1>
</div>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
List of Products
Now we need to display the list of products our store has. We could use a mixed Laravel + Vue approach, but let's keep it a bit simpler. Besides, I kind of promised we're going to be using only (or mostly) frontend technologies.
Let's start by renaming the resources/assets/js/components/ExampleComponent.vue
file into ProductsList.vue
. Then open resources/assets/js/app.js
and replace Vue.component('example-component', require('./components/ExampleComponent.vue'));
with Vue.component('products-list', require('./components/ProductsList.vue'));
. Go back to our view and put this line under the h1
title:
<products-list></products-list>
Boot up the npm watcher by running npm run watch
or simply rebuild your assets once by running npm run dev
. Refresh your page and you should see the example Vue component under the header of your page. Now let's work on ProductsList.vue
. First, go ahead and edit the <script>
section like this.
<script>
export default {
data() {
return {
items: [
{
id: 1,
title: 'Children of Bodom - Hatebreeder',
price: 9.99
},
{
id: 2,
title: 'Emperor - Anthems to the Welkin at Dusk',
price: 6.66
},
{
id: 3,
title: 'Epica - The Quantum Enigma',
price: 15.99
},
{
id: 4,
title: 'Chthonic - Takasago Army',
price: 14.00
},
{
id: 5,
title: 'Silencer - Death - Pierce Me',
price: 1.20
},
{
id: 6,
title: 'My Dying Bride - 34.788%... Complete',
price: 10.00
},
{
id: 7,
title: 'Shape of Despair - Shades of',
price: 7.80
},
{
id: 8,
title: 'Ne Obliviscaris - Portal of I',
price: 11.30
},
{
id: 9,
title: 'Protest the Hero - Fortress',
price: 5.55
},
{
id: 10,
title: 'Dark Lunacy - Devoid',
price: 6.00
},
]
};
}
}
</script>
So we've just created a fake list of products our store has in stock. In real life you would fetch it from your server from a database, but we're keeping things simple. Supposedly our store sells audio CDs. Now let's edit the <template>
section to display the list.
<template>
<table>
<thead>
<th>Name</th>
<th>Price</th>
<th></th>
</thead>
<tbody>
<tr v-for="item in items" :key="item.id">
<td v-text="item.title"></td>
<td>${{ item.price.toFixed(2) }}</td>
<td>Add to Cart</td>
</tr>
</tbody>
</table>
</template>
This is all basic Vue stuff and hopefully you don't have any questions so far. Re-build your assets, refresh the page and you should see all the products listed in the form of a table.
Setting up Vuex
As you've probably noticed, we have a Vue component for a list of products. We also have a cart dropdown in the top navbar of our page, which also will be a separate Vue component. When we add a product to the cart from the products list component, the relevant changes should be reflected in the navbar's cart component. In other words, our components should communicate with each other.
You might've heard about achieving this goal via global events or global variables. This time, though, we'll go with Vuex, which "serves as a centralized store for all the components in an application". What it means for us is we'll store the list of products in the cart in a Vuex store and then every component we have we'll be able to easily access and modify this data. The data is also automatically reactive.
Let's start by importing Vuex inside the bootstrap.js
file:
import Vuex from 'vuex';
...
window.Vuex = Vuex;
Vue.use(Vuex);
Then make these changes to app.js
:
import store from './store.js';
new Vue({
el: '#app',
store: new Vuex.Store(store)
});
We're separating the Vuex storage into its own file here to keep app.js
cleaner. Create the resources/assets/js/store.js
file with the following content:
let store = {
};
export default store;
So a Vuex store is just an object with a set of specific properties. Let's start filling this object with the state
property. state
is kind of equivalent to the data
property of a Vue component. In our state we'll store an array of products added to the cart and an integer count of these products.
let store = {
state: {
cart: [],
cartCount: 10,
},
};
I temporary set the default count to 10 so that we could try to read and display that value in our navbar. Let's create a new Vue component resources/assets/js/components/Cart.vue
. Register this component in app.js
:
Vue.component('cart-dropdown', require('./components/Cart.vue'));
We're going to cut a piece of HTML from the view and paste it into the new component. So this is Cart.vue
:
<template>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="">
Cart (0)
</a>
<div class="navbar-dropdown is-boxed is-right">
<a class="navbar-item" href="">
Cart is empty
</a>
<hr class="navbar-divider">
<a class="navbar-item" href="">
Checkout
</a>
</div>
</div>
</template>
<script>
export default {
}
</script>
Go to welcome.blade.php
, find the piece we've just cut out and replace it with <cart-dropdown></cart-dropdown>
. Refresh your page and you shouldn't see any changes. Now in our component there's a "0" indicating the number of products in the cart at any given moment. Let's display this number directly from the Vuex storage. Edit the corresponding part of template like this:
<a class="navbar-link" href="">
Cart ({{ $store.state.cartCount }})
</a>
Every time we'll change the cartCount
state, the UI will automatically update. Pretty neat, isn't it? Now let's go back to store.js
and set the value to 0, as it should be. And, that's it for now. We'll implement the actual cart in the next part.
All the materials at voerro are absolutely free and are worked on in the author's spare time. If you found any of the tutorials helpful, please consider supporting the project. Thank you!