35

In my code I am using typeahead.js. I use Laravel 5 and I need to replace the var states with my {{ $jobs }} variable. I need to list all Job Titles as an array.

In my controller I have

$jobs = Job::all(['job_title']);

I know the loop method in javascript but I dont know how to "link" my blade's variable in the javascript. Anyone knows how to?

I have tried, in my.js

var jobs = {{ $jobs }}

But that wont work.

Jignesh Joisar
  • 13,720
  • 5
  • 57
  • 57
Sylar
  • 11,422
  • 25
  • 93
  • 166
  • My question was asked first (7yrs + 1 month ago). The other was was only 7 years ago. How comes mine was closed? – Sylar May 17 '22 at 19:00
  • 3
    The duplicate has more votes (both for the question and the answers). Age is not the only factor for the direction of a close vote. – Mark Rotteveel May 20 '22 at 10:52

11 Answers11

42

For more complex variable types like arrays your best bet is to convert it into JSON, echo that in your template and decode it in JavaScript. Like this:

var jobs = JSON.parse("{{ json_encode($jobs) }}");

Note that PHP has to run over this code to make it work. In this case you'd have to put it inside your Blade template. If you have your JavaScript code in one or more separate files (which is good!) you can just add an inline script tag to your template where you pass your variables. (Just make sure that it runs before the rest of your JavaScript code. Usually document.ready is the answer to that)

<script>
    var jobs = JSON.parse("{{ json_encode($jobs) }}");
</script>

If you don't like the idea of doing it like this I suggest you fetch the data in a separate ajax request.

lukasgeiter
  • 147,337
  • 26
  • 332
  • 270
  • 1
    Thanks for that but I got this in the console: `SyntaxError: JSON.parse: expected property name or '}' at line 1 column 2 of the JSON data` – Sylar Mar 27 '15 at 19:28
  • Can you take a look at the source code of the page and see what's actually passed to `JSON.parse`? – lukasgeiter Mar 27 '15 at 19:30
  • It points to `var jobs = JSON.parse("{{ json_encode($jobs) }}")` – Sylar Mar 27 '15 at 19:32
  • But this is inside a blade template isn't it? – lukasgeiter Mar 27 '15 at 19:33
  • No. I have created a .js file and put the code there. – Sylar Mar 27 '15 at 19:35
  • Well that is obviously not gonna work. If you want to pass PHP variables to JavaScript you have to do it where PHP actually runs. – lukasgeiter Mar 27 '15 at 19:35
  • So I have to put the js in my page.blade.php? – Sylar Mar 27 '15 at 19:49
  • Yes. you know PHP and JavaScript can't really talk to each other. What we're doing here is actually just generating JavaScript code (the variable definition) with PHP and echoing the variable out. So you have to do it somewhere PHP runs. And in a separate JS file that's not the case. I hope I explained that well ;) – lukasgeiter Mar 27 '15 at 19:52
  • Ok let me make few changes. Thanks. – Sylar Mar 27 '15 at 20:38
  • 1
    @lukasgeiter can you not simply `var jobs = {!! json_encode($jobs); !!}; `? – Peter Chaula Mar 31 '17 at 16:07
  • If some string inside the array $jobs has characters this solution won't work. – PaoloCargnin Apr 02 '18 at 13:32
  • This is a very good explanation and very helpful for people who are confused why there is an syntax error when they have the variable is accessed in a separate JS file. Also var jobs = {!! json_encode($jobs); !!}; works. – Deepak Daniel Mar 18 '21 at 17:28
32

This works for me

jobs = {!! json_encode($jobs) !!};

Notice

Only use this method if you can guarantee that $jobs contains no user input values. Using {!! ... !!} outputs values without escaping them which could lead to cross-site scripting (XSS) attacks.

Advena
  • 1,664
  • 2
  • 24
  • 45
Karim Samir
  • 1,470
  • 17
  • 17
18

in laravel 6 this works for me

using laravel blade directive

var jobs = {!! json_encode($jobs) !!};

also used @json directive

var jobs = @json($jobs);

using php style

 var jobs = <?php echo json_encode($jobs); ?>
Jignesh Joisar
  • 13,720
  • 5
  • 57
  • 57
5

Just to add to above :

var jobs = JSON.parse("{{ json_encode($jobs) }}");

will return escaped html entities ,hence you will not be able to loop over the json object in javascript , to solve this please use below :

var jobs = JSON.parse("{!! json_encode($jobs) !!}");

or

var jobs = JSON.parse(<?php echo json_encode($jobs); ?>);

Jimmy Obonyo Abor
  • 7,335
  • 10
  • 43
  • 71
4

In laravel 7 I just do

var jobs = '{{ $jobs }}';
Saddan
  • 1,546
  • 16
  • 19
3

A simple way is by using the new @json() directive of Blade.

✓ Tested on Laravel 7.x

<script>
    let myVariable = @json($myVariable)
    console.log(myVariable)
</script>
Gass
  • 7,536
  • 3
  • 37
  • 41
2

this approach work for me:

var job = {!! json_encode($jobs ) !!}

and use in java script

behnam shateri
  • 1,223
  • 13
  • 18
1

In the newer laravel versions, in json "(double quotes) gets encoded into htmlentity as &quote; and unwanted JSON.parse() call by using this,

var jobs = JSON.parse("{{ json_encode($jobs) }}");

and you'll end up with the javascript error. Alternatively, you can use

var jobs = {!! json_encode($jobs, JSON_PRETTY_PRINT) !!};

As we know, {!! !!} is meant for printing HTML and it will work like a charm. This will reduce the overhead of parsing JSON at the browser as well.

Kiran Maniya
  • 8,453
  • 9
  • 58
  • 81
0

I just solved this by placing a reference on the window Object in the <head> of my layout file, and then picking that reference up with a mixin that can be injected into any component.

TLDR SOLUTION

.env

GEODATA_URL="https://geo.some-domain.com"

config/geodata.php

<?php

return [
    'url' => env('GEODATA_URL')
];

resources/views/layouts/root.blade.php

<head>
    <script>
        window.geodataUrl = "{{ config('geodata.url') }}";
    </script>
</head>

resources/js/components/mixins/geodataUrl.js

const geodataUrl = {
    data() {
        return {
            geodataUrl: window.geodataUrl,
        };
    },
};

export default geodataUrl;

usage

<template>
    <div>
        <a :href="geodataUrl">YOLO</a>
    </div>
</template>

<script>
import geodataUrl from '../mixins/geodataUrl';

export default {
    name: 'v-foo',

    mixins: [geodataUrl],

    data() {
        return {};
    },

    computed: {},

    methods: {},
};
</script>

END TLDR SOLUTION

If you want, you can use a global mixin instead by adding this to your app.js entrypoint:

Vue.mixin({
    data() {
        return {
            geodataUrl: window.geodataUrl,
        };
    },
});

I would not recommend using this pattern, however, for any sensitive data because it is sitting on the window Object.

I like this solution because it doesn't use any extra libraries, and the chain of code is very clear. It passes the grep test, in that you can search your code for "window.geodataUrl" and see everything you need to understand how and why the code is working.

That consideration is important if the code may live for a long time and another developer may come across it.

However, JavaScript::put([]) is in my opinion, a decent utility that can be worth having, but in the past I have disliked how it can be extremely difficult to debug if a problem happens, because you cannot see where in the codebase the data comes from.

Imagine you have some Vue code that is consuming window.chartData that came from JavaScript::put([ 'chartData' => $user->chartStuff ]). Depending on the number of references to chartData in your code base, it could take you a very long time to discover which PHP file was responsible for making window.chartData work, especially if you didn't write that code and the next person has no idea JavaScript::put() is being used.

In that case, I recommend putting a comment in the code like:

/* data comes from poop.php via JavaScript::put */

Then the person can search the code for "JavaScript::put" and quickly find it. Keep in mind "the person" could be yourself in 6 months after you forget the implementation details.

It is always a good idea to use Vue component prop declarations like this:

props: {
    chartData: {
        type: Array,
        required: true,
    },
},

My point is, if you use JavaScript::put(), then Vue cannot detect as easily if the component fails to receive the data. Vue must assume the data is there on the window Object at the moment in time it refers to it. Your best bet may be to instead create a GET endpoint and make a fetch call in your created/mounted lifecycle method.

I think it is important to have an explicit contract between Laravel and Vue when it comes to getting/setting data.

In the interest of helping you as much as possible by giving you options, here is an example of making a fetch call using ES6 syntax sugar:

routes/web.php

Route::get('/charts/{user}/coolchart', 'UserController@getChart')->name('user.chart');

app/Http/Controllers/UserController.php

public function getChart(Request $request, User $user)
{
    // do stuff
    $data = $user->chart;

    return response()->json([
        'chartData' => $data,
    ]);
}

Anywhere in Vue, especially a created lifecycle method:

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

methods: {
    async handleGetChart() {
        try {
            this.state = LOADING;
            const { data } = await axios.get(`/charts/${this.user.id}/coolchart`);

            if (typeof data !== 'object') {
                throw new Error(`Unexpected server response. Expected object, got: ${data}`);
            }

            this.chartData = data.chartData;
            this.state = DATA_LOADED;
        } catch (err) {
            this.state = DATA_FAILED;
            throw new Error(`Problem getting chart data: ${err}`);
        }
    },
},

That example assumes your Vue component is a Mealy finite state machine, whereby the component can only be in one state at any given time, but it can freely switch between states.

I'd recommend using such states as computed props:

computed: {
    isLoading() { return (this.state === LOADING); },
    isDataLoaded() { return (this.state === DATA_LOADED); },
    isDataFailed() { return (this.state === DATA_FAILED); },
},

With markup such as:

<div v-show="isLoading">Loading...</div>
<v-baller-chart v-if="isDataLoaded" :data="chartData"></v-baller-chart>
<button v-show="isDataFailed" type="button" @click="handleGetChart">TRY AGAIN</button>
agm1984
  • 15,500
  • 6
  • 89
  • 113
0

In Laravel 8. You can use blade template por this...

const jobs = @json($jobs ?? NULL);
2Plug
  • 1
0
var job_id = '{{ $jobs->first()->id}}';
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Mostafa EGY
  • 19
  • 1
  • 5
  • 1
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. You can find more information on how to write good answers in the help center: https://stackoverflow.com/help/how-to-answer . Good luck – nima Oct 10 '21 at 11:17
  • This will cause problems if the value has single quotes in it – Youness Jan 11 '23 at 17:04