reinink / client-side-rendering-in-laravel

199 stars 36 forks source link

Passing backend data to Vue #10

Open diemah77 opened 5 years ago

diemah77 commented 5 years ago

I wonder what is the best way to pass some data like logged-in user or breadcrumbs created by laravel to Vue. By now I'm merging all of the data and passing it through props to each of the page components. But it feels like a hack and I think there might be a better solution. What would you suggest?

Juhlinus commented 5 years ago

In your app/Providers/AppServiceProvider.php you can do something along the following:

View::composer('*', function ($view) {
    if (Auth::user()) {
        $view->with('shared', [
            'auth' => [
                'user' => [
                    'id' => Auth::user()->id,
                    'name' => Auth::user()->name,
                    'email' => Auth::user()->email,
                ],
            ],
        ]);
    }
});

And in your resources/views/app.blade.php just below your <body> tag:

<script>
    window.shared = @json($shared ?? []);
</script>
diemah77 commented 5 years ago

That's all true and correct for the authenticated user, as the shared data doesn't change for all admin routes. But the issue arises when passing breadcrumbs to the window object. In this case turbolinks doesn't override the breadcrumbs for each route, but creates an addditional script tag within the head tag.

By now I'm passing the breadcrumbs collection to each page component and then to the root layout component where breadcrumbs are rendered. Ideally I would like to access them globally.

Juhlinus commented 5 years ago

That would be more clean approach, yes. As far as I understood it from Twitter @reinink is dropping Turbolinks entirely and going with a straight Vue and Laravel approach. Previously he mentioned something along the lines of "TurboVue", but I swear I saw a tweet stating that it will be dropped for a more favorable and straightforward solution.

Using this repository as a base seems a bit clunky when it comes to issues like this. I'm currently using this in one of my projects, but have prepared myself for a refactor when the aforementioned solution comes.

diemah77 commented 5 years ago

For those interested, I now do something like this:

render: h => h(
            Vue.component('admin'), {
                props: JSON.parse(root.dataset.props)
            }
      )

I'm creating the layout component and passing the page component name as prop:

ViewFactory::macro('component', function ($name, $data = []) use ($auth) 
        {
            $data['name'] = $name;
            $data['auth'] = $auth;

            return View::make('dashboard', [
                'data' => $data, 
            ]);
        });

In the layout component I have a dynamic component that renders the passed page component:

<transition tag="div" name="fade" appear>
    <component :is="data.name" :parentData="childProps" @title="setTitle"></component>
</transition>

where props and childProps look like this:

props: {
    data: {
        type: Object,
        required: true
    }
},

computed: {
    childProps()
    {
           let props = Object.assign({}, this.data)

        delete props.auth
        delete props.breadcrumbs
        delete props.name

        return props
    }
}
theprobugmaker commented 4 years ago

What this @ means on @json? Is that from blade or Vue?

reinink commented 4 years ago

@zefexdeveloper That's a Blade directive for outputting a PHP variable as JSON.

theprobugmaker commented 4 years ago

The same for @title? I'm asking this cause It's kinda confusing. I mean, if I have an array being passed down to the view I could just do something like :tags="{{ $tags }}" and then Vue can traversal it, right?

reinink commented 4 years ago

No, @title is in a Vue component, and that's an event. And yes, that is confusing...two different languages/frameworks.

If you're looking at this repo, I highly recommend checking out my Inertia.js project. That's really where the thinking from this repo (blog post) lead me.

theprobugmaker commented 4 years ago

@reinink Thank you very much, I heard about Inertia.js before and it looks amazing, I will definitely take a look and probably refactor the project later.

Right now I'm using inline components with Vue because it sucks to pass down localization information down from Laravel to Vue components. It will do the trick now, gotta get things done but later I will definitely refactor and make it better.