Vue Components with Laravel Based APIs

LaravelVue.jsPosted on

Vue components are a vital part of SPAs or hybrid apps. Usually, these components aren’t working with static data but fetched information and data from a back-end API. But every component has its own job, its own life-cycle, so we can’t fetch all the data by one AJAX call. Let’s see how to break these calls apart and make the components standalone.

General Overview

We are going to work with Vue and Laravel as a hybrid web app. Let’s say we have a webshop, where we want two standalone widgets. A widget for the most popular products and another one for the recently bought items.

How should it work then? First, we load the homepage, where we place the Vue components in the blade file, we load the data from the API. If the XHR request returns a response 200, we fill our component data. Then the two-way data binding does the rest.

Setting Up Our Environment

For the asset handling, we are using Laravel’s mix. By default the package.json contains the Vue and the axios so all we have to do is install the packages via npm.

Let’s say we have a ready-to-use API on the back-end. The API validation is token based, what we can configure at the bootstrap.js. To make sure the CSRF token and the API token is passed for all the requests, we need to extend the window.Laravel object.

// before the closing body tag

<script>
    window.Laravel = {!! json_encode([
        'csrfToken' => csrf_token(),
        'apiToken' => auth()->user()->api_token ?? null,
    ]) !!};
</script>

Now we can setup our bootstrap.js, import and configure what we need here. We add our components here too a little bit later.

// resources/assets/js/bootstrap.js

import Vue from 'vue';
import axios from 'axios';

window.Vue = Vue;

axios.defaults.headers.common['X-CSRF-TOKEN'] = Laravel.csrfToken;
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.headers.common['Authorization'] = 'Bearer ' + Laravel.apiToken;

window.axios = axios;
If you want to use the axios’ api like calling this.$http, all you have to do is the following snippet: window.Vue.protype.$http = axios.

Now the bootstrapping is ready, the configuration is set, we can create our components.

On the back-end side, we need to setup the API routes and the proper controller methods for them. Let’s assume we have a ProductsController what responsible to handle the API calls for the products.

// routes/api.php

<?php

Route::get('products/latest', 'ProductsController@latest')->name('products.latest');
Route::get('products/popular', 'ProductsController@popular')->name('products.popular');
// app/Http/Controllers/ProductsController.php

<?php

namespace App\Http\Controllers;

use App\Product;
use Illuminate\Http\Request;

class ProductsController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth:api');
    }
    public function latest() 
    {
        $products = Product::latest()->take(5)->get();

        return response()->json($products);
    }

    public function popular() 
    {
         $products = Product::orderBy('transactions.count', 'desc')->take(5)->get();

         return response()->json($products);
    }
}
To activate the API side authentication, we need to add the “auth:api” middleware in our constructor.

Let’s say we also have a Transaction model where we created the relationship with the Product model. So now we can order the products by the related transactions.

Both of our controller methods are returning with an Illuminate\Http\Response instance where the content type header is set to “application/json”. It’s important because of the easy JSON parsing from the front-end side.

Creating the Components

To be honest, we need only one Vue component, where the API endpoint is changeable and we can give what we want. But that’s easy with component properties, so let’s stick with only one component what we use as many times as we want.

First of all, let’s reverse the things and define what we want to have as a result. We want to pass our components their own endpoints, but it would be good if that would be dynamic.

<product-list resource="{{ route('api.products.latest') }}"></product-list>

By this approach, we can use Laravel’s URL helpers, what makes the dynamic URL generation easier. So let’s see our component then we explain what is going on.

By using the .vue extension, we are able to store our template and the JavaScript in one file for a specific component.
// resources/assets/js/components/ProductList.vue

<template>
    <ul>
        <li v-for="product in products" v-text="product.name"></li>
    </ul>
</template>

<script>
export default {
    props: ['resource'],

    created() {
        this.getProducts();
    },

    data() {
        return {
            products: []
        };
    },

    methods: {
        getProducts() {
            axios.get(this.resource)
                .then(response => this.products = response.data)
                .catch(error => {});
        }
    }
}
</script>
Inside of a template, we can only use the v-text directive to print text inside of a DOM element. The use of {{ }} is not working in this case.

Quickly go through on the template tag. It’s nothing special but some HTML and Vue bindings. When the products array is populated, we list the items with a v-for directive. There is no styling included for these, our aim is to show the methods only.

Now examine the script tag. Here we define four things, what our component requires to work the way we want:

We name the accepted props. In this case, the only prop our component accepts the “resource” attribute, where we pass the proper URL for every component.

Vue has the ability to listen for several events, like created or mounted. Now we need the created event, when we want to trigger our AJAX call, to populate “products” array, what we defined in our data object. This is what we list in our template’s v-for directive.

Also, we implement the axios request itself. Axios is a promised based library which makes possible to work asynchronously with our AJAX calls. If the request is successful we populate our “products” array, with the response from the server-side.

Make the Component Accessible

As we mentioned formerly, to use the components, we need to pull them into our Vue instance. Now the proper place is the bootstrap.js, so add the following line at the end of the file:

// end of bootstrap.js

import ProductList from './components/ProductList';
window.Vue.component('product-list', ProductList);

Now Vue can read the component tag, and replace it with the template. All we have to do is to reference the component with the URLs in our blade template.

<product-list resource="{{ route('api.products.latest') }}"></product-list>
<product-list resource="{{ route('api.products.popular') }}"></product-list>

Summary

The main point of this post to show the very basics of the cooperation of a Laravel based API and a Vue component. This approach is essential to modern web applications. It worths to put some focus on this topic in the future.

To read more about Vue components, visit this page. Also, Laracasts has an awesome course about Vue, it explains how components work through real-life examples.

Need a web developer? Maybe we can help, get in touch!

Similar Posts

More content in Laravel, Vue.js category