Laravel routes in your Javascript files

David Emerson by David Emerson

While I was at Laracon this year, a problem I have been having for a long while was brought up. The problem is simply that I cannot use route names in Javascript (or specifically Vue) files. I would prefer to use route names so that I did not have to make any changes should a URI need to change in the future. This is the same reason we use the route() helper in our backend code. So I finally decided to tackle this in between some talks and I found a relatively simple way to do this.

Collect.js

First, include the collect.js javascript library in your package.json.

npm install --save collect.js
// or
yarn add collect.js

RouteComposer

Next, we will need to get our routes. I did this in a view composer so I could send it to my javascript.blade.php partial included in my default layout. If you use the PHP vars to JS package from Jeffrey Way you can put it anywhere you would like. So first I made a RouteComposer. Laravel doesn’t have a make method for these, but they are pretty straight forward since they don’t need to include or extend anything to work. They just need a composer() method that is passed a View instance. Below is mine.

<?php 

namespace App\Http\Composers;

use Illuminate\Contracts\View\View;
use Illuminate\Routing\Route;

class RouteComposer
{
    /**
     * Bind data to the view.
     *
     * @param  View $view
     *
     * @return void
     */
    public function compose(View $view)
    {
        $router = app('router');

        $laravelRouteList = supportCollector($router->getRoutes())
            ->flatMap(function (Route $route) {
                return [$route->getName() => '/' . $route->uri()];
            });

        view()->share(compact('laravelRouteList'));
    }
}

All I am doing is getting a list of routes from the Router and then turning that into a collection with the route name as the key and the route URI as the value.  Now that we have the composer, we need to tell Laravel to use it.

Personally, I have a ComposerServiceProvider in app/Providers.  This is where I bind all my composer classes.  You could do the same thing in something like the AppServiceProvider as well though.  In either class, put the following in the boot method.

If you are using the Javascript utility from Laracasts you could alternately just use JavaScriptFacade::put(compact('laravelRouteList')); here and use window.laravelRouteListin the bootstrap.js section.  If you go this route, feel free to skip down to the bootstrap.js section.

view()->composer(
[
'layouts.partials.javascript',
],
'App\Http\Composers\RouteComposer'
);

As I said, I have a Javascript blade file that is included in my default layout.  Set the first argument to whatever blade file you want that will be included.  Then you point it to your new RouteComposer and Laravel does the rest.

Adding it to a blade file

Now, how do we use this to get the routes to Javascript?  In my javascript.blade.php, I have a Laravel section that sets some common values from Laravel into JS variables.

window.Laravel = <?php echo json_encode([
    'csrfToken'  => csrf_token(),
    'routes'     => $laravelRouteList,
]);

Some people use the meta tag for the csrfToken, but I add it in here. So I have now also added my routes from the composer here so that Javascript can use them.

bootstrap.js

Next up is the resources/assets/js/bootstrap.js file. In here we need to get collect.js pulled in and use it to make the route object more usable.

window.collect = require('collect.js')
window.routes = collect(Laravel.routes)

We need to collect the routes because route names tend to use dot notation, and so do Javascript objects. So to keep the code clean, I wrap them in the new collect.js package.

Using route names in JS

This allows us to finally use our route names in our Javascript or Vue files. Here’s a quick example.

axios.get(routes.get('admin.dashboard'))

Since routes is a Javascript collection, we can just pass the route name to the get method and get back our URI.

Closing

I hope someone else finds this as helpful as I have. If you have any suggestions for improvements, or if this has already been done, feel free to shoot me an email at david@siterocket.com.