0

I have just read this question and noticed a lot of the answers showing json encoding. I'm just wondering why? Is it for some kind of security? So for instance in Laravel would this be the correct way to pull stuff in

var date = '{!! json_encode($event->date) !!}',
parseDate = JSON.parse(date);

//use parseDate here.

Sorry for making a whole new question, but I don't have the rep to comment on the original question :/

Community
  • 1
  • 1
Dalek
  • 111
  • 4
  • 12
  • Your question doesn't seem to have anything to do with the link you posted, other than referencing laravel and some json functions. The person in your link was asking about ways to send and access an Eloquent instance in javascript and a simple, non-trivial way to do so is to json_encode it's attributes and pass that to javascript. – georaldc Jan 16 '17 at 17:18
  • 1
    @georaldc And his question is why is it necessary to json_encode said data. :) – Nicklas Kevin Frank Jan 16 '17 at 17:19
  • Its because javascript variables are stored as json – Paras Jan 16 '17 at 18:08

4 Answers4

4

When you use json_encode as:

var date = {!! json_encode($event->date) !!};

then it returns a JSON string. The PHP json_encode function translates the data passed to it to a JSON string which can then be output to a JavaScript variable. And a JSON is a subset of JavaScript literal notation, and so you can drop JSON into JavaScript code directly anywhere an expression is valid.

No need for JSON.parse or $.parseJSON and in fact, using them would fail.

date will either be a JavaScript object (if the PHP "associative" array has non-numeric keys and so json_encode output {...}) or a JavaScript array (if json_encode output [...]).

Amit Gupta
  • 17,072
  • 4
  • 41
  • 53
0

Generally, JSON is used to send data from the server to the browser when you want to retain it's structure. It is easy to parse in Javascript with the result being an array that matches the original array in the server language (like PHP). For simple, single values, it isn't really needed.

Sloan Thrasher
  • 4,953
  • 3
  • 22
  • 40
0

In response to Amit Gupa, here is the code that won't work without a parse (based on this). Okay the first part is the main part of the view. The $event is being pulled from the database with date and time stored separate. I'm using this as a widget using laravel widgets although it pretty much acts as a normal view.

<div id="clockdiv">
        <div class = 'timeFormat form-group-shadow'>
            <span class="days">0</span>
            <div class="small-text">Days</div>
        </div>
        <div class = 'timeFormat form-group-shadow'>
            <span class="hours">00</span>
            <div class="small-text">Hours</div>
        </div>
        <div class = 'timeFormat form-group-shadow'>
            <span class="minutes">00</span>
            <div class="small-text">Minutes</div>
        </div>
        <div class = 'timeFormat form-group-shadow'>
            <span class="seconds">00</span>
            <div class="small-text">Seconds</div>
        </div>
    </div>
</div>

{{ HTML::script('js/countdown.js') }}

<script>
    var date = '@if(isset($event)){!! json_encode($event->date) !!}@endif',
    time = '@if(isset($event)){!! json_encode($event->time) !!}@endif',
    dateTime = JSON.parse(date) + " " + JSON.parse(time);

   if(new Date(dateTime) >= Date.now()){
       initializeClock('clockdiv', dateTime);
   };
</script>

The script 'js/countdown.js' can be seen here.

function getTimeRemaining(endtime){
    var t = Date.parse(endtime) - Date.parse(new Date());
    var seconds = Math.floor( (t/1000) % 60 );
    var minutes = Math.floor( (t/1000/60) % 60 );
    var hours = Math.floor( (t/(1000*60*60)) % 24 );
    var days = Math.floor( t/(1000*60*60*24) );
    return {
        'total': t,
        'days': days,
        'hours': hours,
        'minutes': minutes,
        'seconds': seconds
    };
}

function initializeClock(id, endtime){
    var clock = document.getElementById(id);
    var daysSpan = clock.querySelector('.days');
    var hoursSpan = clock.querySelector('.hours');
    var minutesSpan = clock.querySelector('.minutes');
    var secondsSpan = clock.querySelector('.seconds');

    function updateClock(){
        var t = getTimeRemaining(endtime);
        daysSpan.innerHTML = t.days;
        hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
        minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
        secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
        if(t.total<=0){
            clearInterval(timeinterval);
        }
    }

     updateClock();

     var timeinterval = setInterval(updateClock,1000);
}

This code is working fine, but fails when I don't parse.

Dalek
  • 111
  • 4
  • 12
  • If you do `JSON.parse('2017-01-25')` in Javascript then it will fail. You don't need `JSON.parse` or `$.parseJSON` when you are doing `json_encode`. – Amit Gupta Jan 25 '17 at 14:26
0

I was just suffering this issue sending data from a Laravel API to a Vue SPA, via the "login using oauth" callback blade template.

It took me some time to figure out how exactly to fix the payload. I found the answer in this question thanks to Amit Gupta's answer.

Here is the payload exiting Laravel, for context:

    return view('oauth/callback', [
        'user' => $user,
    ]);

Then, in callback.blade.php, I have this code:

<html>
    <head>
        <meta charset="utf-8">
        <title>{{ config('app.name') }}</title>
        <script>
            window.opener.postMessage(
                {
                    user: {!! $user !!},
                },
                "{{ url('/') }}"
            );

            window.close();
        </script>
    </head>

    <body>
    </body>

</html>

I had to change this:

user: "{{ $user }}",

to this:

user: {!! $user !!},

Before the fix, I was getting a stringified object interpolated into the string, so I was plagued with SyntaxError: Unexpected token o in JSON at position 1 errors, because the { and } characters weren't encased in double-quotes.

Here is my JavaScript that receives the payload:

    onMessage(e) {
        const hasUser = (e.data.user && (Object.keys(e.data.user).length > 0));

        if ((e.origin !== window.origin) || !hasUser) {
            return undefined;
        }

        console.log('user payload', e.data.user);
        console.log('type', typeof e.data.user);

        if (this.hasIntendedUrl) {
            return this.$router.push(this.$store.getters['auth/intendedUrl'])
                .then(() => this.$store.dispatch('auth/clearIntendedUrl'))
                .catch(() => {});
        }

        return this.$router.push({ name: 'home' }).catch(() => {});
    },

You can see I didn't require to JSON.parse() because typeof e.data.user is object.

Sorry for the long answer here, but I wanted to show a fully-qualified solution. To me this is simply a type-casting issue based around first JSON stringifying the payload, and then JSON parsing it. This implies that Laravel should focus on preparing a transport-safe version of the object, and that JavaScript should focus on transforming it back to an Object.

It is always a tricky slope to mix PHP and JS in the same file. As you can see in my example, those innocuous " wrapping "{{ $user }}" was JavaScript code being instructed to put the object into a string, and that is something that cannot be JSON.parse()'d.

agm1984
  • 15,500
  • 6
  • 89
  • 113