Javascript in Laravel (single Vue instance)
A single Vue instance
Update May 2021
Please note that this post is currently 3 years old. As you know, things move fast in our tech world and event though the principles described in this post are still valid, there might be better ways to achieve the same result; You are free to continue reading, but I recently wrote a post about our current approach.
Additionally, Laravel 8 brings a new starter kit to quickly scaffold the frontend of your next project. It’s called Laravel Jetstream and is definitely worth taking a look. I’ll choose Laravel Jetstream with Inertia scaffolding for my next project.
Therefore, I am removing the code repository that originally came with this post.
Original post
This is the fourth post in our Javascript in Laravel series. We have discussed the series overview and sample project, discussed the jQuery way and continued with Laravel Mix as module bundler. Finally we introduced Vue.
- Overview of the series and sample project
- Applying Javascript the jQuery way
- Using Laravel Mix and Webpack as a module bundler
- Installing Vue in a Laravel application
In this post we’ll use Vue to implement the Javascript behaviour we initially developed with jQuery. We will create a single Vue instance for our sample application that will be loaded in the main layout file app.blade.php
.
The details of the code
Starting from the previous jQuery code base (branch introduce-webpack), we need to import Vue to start. We’ll do this in the bootstrap.js
file and make it a global variable by attaching it to the window.
import Vue from vue;
window.Vue = Vue;
Next up is app.js. We require the bootstrap.js file and create a new Vue instance like so
const app = new Vue({
el: '#app',
data: {},
methods: {},
});
To be able to implement the same behaviour as before, we need to make some changes:
- Add v-model directives to the form.
- Add a loading indicator and make sure it is shown at the right moment.
- Add info/ error messages for the user to know if all input is ok.
Below is the new version of the form form.blade.php
that can be used by Vue
Then it is time to show the Vue code that makes the blade view above interactive:
As you can see in the data
section of the code, we have a boolean for the loading indicator in l. 24 of form.blade.php
.
Furthermore, the following 4 variables (gender, email, zipcode and country) are used for the 4 form fields:
gender
is used as a v-model for the radiobuttons where the user can select their gender.email
is an object, the value property is the v-model for the email input field, and themsg
andclass
properties are for showing the info or error message in the right way.- The same holds true for
zipcode
andcountry
.
In form.blade.php
we added various event callbacks that are summarised in the methods
object. The logic of these callback functions is comparable to the jQuery version discussed earlier. The main difference is that we no longer need to manipulate the DOM directly. Instead, we change the data property in Vue and this causes a rerender of (parts of) the form.
In general I think that implementing Javascript behaviour this way makes much more sense to the brain. We just change some data (based on the business logic of the app) and the rest is taken care of by Vue.
Things to think about
In this very simple example we only have a single page which needs some Javascript.
Typically, a web application consists of multiple pages, each with some Javascript applied to them. Continuing the way discussed so far would lead to an app.js
file that gets very large. Additionally, the number of items in the data and method objects would increase dramatically, which makes the code hard to maintain.
So, perhaps we could use the single Vue instance per page approach?
A single Vue instance per page
This approach means that we create an app.js
file for each page (of course we name these files differently 😊). Subsequently, we need to update the webpack.mix.js
file to be able to compile all these files. That is quite easy though, we simply add some more rows like this
mix.js('resources/assets/js/page1.js', 'public/js')
.js('resources/assets/js/page2.js', 'public/js')
.js('resources/assets/js/page3.js', 'public/js')
.js('resources/assets/js/page4.js', 'public/js')
And then we would use the compiled version directly in the view like so. Note that we no longer load app.js in the main layout as each page gets its own version:
<!-- page1.blade.php -->
@extends('layouts.app')@section('content')
...<script src="{{ mix('/js/page1.js') }}"></script>
@stop
This approach makes sure that only the Javascript code for the page we are on is loaded and nothing more. However, if we are using the same datepicker on multiple pages, a lot of code repetition is needed to make it work.
I have used this approach in a personal project. Although it works, the code is rather difficult to maintain and it seems overly complex when I look at this code now.
We definitely need components
In an upcoming post, we’ll discuss an improved approach I call the single Vue instance for the entire application approach. We still instantiate a single Vue instance as we have done here, but this instance does not hold data and/ or methods. Instead, it imports single file components which may be used throughout the blade views in the application.