Laravel & VueJS variable definition - php

I decided to create a little project using Laravel 5.1, vueJS (with vue-resource) and some file interaction on the server-side.
Everything works fine but in the process of creating the app I questioned myself what the best way of receiving data from the server was. Besides the ovious solution, AJAX, I tried writing the data with laravel-blade inside the data object of my Vue instance, which looked like this:
FileController.php:
$files = Auth::user()->files;
return view('files.index', compact('files'));
The files() function is just a simple belongsToMany() relationship on the User Model with a pivot table in the background.
index.blade.php:
new Vue({
el: '#files',
data: {
// pass the data to Vue via PHP so you don't need an AJAX request
files: {!! $files !!}
// ..
}
The other solution would be something like this in my Vue instance:
ready: function() {
this.$http.get('/todos', function (data) {
this.$set('todos', data);
}).error(function (data) {
console.log(data);
});
Now my question is whether or not my approach has any disadvantages because in my opinion it's better because it's less AJAX interaction and that means the app should be faster (at least in theory).
Edit: of course it's possible to simply write it with a blade foreach for example but I want to do the presentation of the data in vue.

You can get the data using the props:
<div id="files" :files="{{ $files }}"></div>
And in your Vue instance:
new Vue({
el: "#files",
props: ['files'],
data: {
list: []
},
ready: {
this.list = JSON.parse(this.files)
}
});
The props option files is filled by the $files from laravel, and is parsed to json to use in vue, after the ready option, you can use in HTML:
<div v-for="l in list">
#{{ l.name }}
#{{ l.last_name }}
</div>
The list contains the json with the files that comes from Laravel controller.

You need props
So your element will look like:
<div id="#files" :files="{!! $files !!}"></div>
And your Vue:
new Vue({
el: '#files',
props: {
files: {
type: Object // <- not necessary, you can also have props: ['files']
}
}
});

Related

Accessing Vue JS component data inside PHP using JQuery

I have a vue js application which is compiled and rendered in its own enviroment within my php project. I use a old version of a PHP framework and not Laravel.
My vue js application instance is created in the main.js file
Vue.component(
'Dashboard',
require('./components/dashboard/Index.vue').default
);
console.log(window.location);
const router = new VueRouter({
mode: "history",
base: window.location.pathname,
});
var myVueInstance = new Vue({
router
}).$mount('#app');
and then i use this compiled vue js application just before the closing tag in my project.
<script src="<?php echo getenv('APP_DEBUG') == 'false'
? Yii::app()->baseUrl.'/js/vue/js/main.min.js'
: 'http://localhost:8080/js/main.js' ?>"></script>
The issue is that i am trying to inject and set a data key inside one of the components using the ref name but i cannot get hold of anything to set the key.
<div id="app">
<quote-upload ref="quoteUpload"></quote-upload>
<view-quote ref="viewQuote"></view-quote>
<invoice-upload ref="invoiceUpload"></invoice-upload>
</div>
var self = this;
$(document).on("click", ".openQuoteModal", function () {
myVueInstance.$refs.quoteUpload.vehicleid = $(this).data('id'); //this doesnt work.
});
but i am unable to do so as it is always undefined.
Is there any way to do this?
I tried the following but still got the my vue instance variable was undefined. Strange that it is because it is above the call i do.
I ended up listen for the click within the vue component itself when the vue component got mounted. I got the value on click and then set the vehicleid internally within the component.
mounted() {
var self = this;
this.$nextTick(() => {
$(document).on("click", ".quoteUploadModal", function(){
self.vehicleid = $(this).closest('.openQuoteModal').data('id');
});
});
},

How to use Vue on a site with multiple pages?

I have a project in Laravel where I import my app.js is:
require('./bootstrap');
window.Vue = require('vue');
const app = new Vue({
el: '#app',
data: {},
methods: {},
});
This file is imported in my main layout (app.blade.php) like so:
<div id="app">
#yield('content')
</div>
<script src="{{asset('js/app.js')}}"></script>
#yield('scripts')
I guess you can't instantiate a new Vue instance within another one... so I MUST work with a single Vue instance. The #app div encompasses all of the dynamic content of my multiple-page application, and so my question is... on different pages, how do I separate my Vue.js code? If I put all of the code in app.js, it will get cluttered, and my code will have missing references depending on which page the user is on.
I tried writing a separate <script></script> for each page, but this requires me to input the CSRF token for each page, but this feels DRYish.
How do people do this?
Your route should be like this to accept vue router
Route::get('/{view?}', function () {
return view('vue');
})->where('view', '^(?!api\/)[\/\w\.-]*');
this will accept every route exept api routes

How to structure Vue.js instances in a Laravel project?

I have an application with multiple web pages. On each webpage, I want to be able to bind a Vue instance to some element on the page as to give it a more dynamic user interaction.
In the base Laravel project, you're supposed to bind a Vue instance to #app in app.js which I've done. Including any more instances, however, give a "element not found" error when you're on any page that doesn't include that element. I imagine you're not supposed to put all of your instances in app.js...
What is the general architecture for having a different Vue instance on each page? Do I make a new js file for each page, and include it?
Sorry for my naivety!
You can create a new Vue instance on a yielded scripts section in app.blade.php that every view extends (mostly) for each page where needed to avoid the Vue warning
For example, Laravel Vapor website does* this, in the main page you can find this code
*they use a CDN to include the library instead of the compiled one from webpack
A Vue instance just for the Form
<script>
var app = new Vue({
el: '#form',
data: {
email: '',
submitted: false,
submitting: false
},
methods: {
onSubmit: function () {
this.submitting = true;
fetch('/api/early-access', {
method: 'POST',
body: JSON.stringify({ email: this.email }),
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
this.submitted = true;
this.submitting = false;
});
}
}
})
</script>
In layouts/app.blade.php yield a section for the scripts right before the closing tag of the body, for example
<main class="py-4">
#yield('content')
</main>
</div>
#yield('scripts')
</body>
Then instantiate a new Vue instance in the Blade view file of a page like so
#extends('layouts.app')
#section('content')
<div class="container">
<div id="home-page"></div>
</div>
#endsection
#section('scripts')
<script>
new Vue({
el: '#home-page',
});
</script>
#stop
Hope this helps
I just use a single Vue instance.
I have a master blade layout in which the body section looks like the code given below and I extend it for all the pages. It's basically a template with head tag filled with #yield('tags_like_meta', default_value) and some other javascript imports.
<body>
<div id="app">
#yield('contents')
</div>
</body>
And then I have a single Vue instance like this on my js file.
const app=new Vue({el:"#app"});
For different pages I use Vue-router to dynamically load the component. For example, if I want my contact page to be loaded via Vue, I will have my contact_page.blade.php look like this
#extends(layout.master)
#section('contents')
<router-view></router-view>
#endsection
And Vue-router will handle the rendering if I have the contact page url specified in the routes.
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/contact',
name: 'contact',
component: () => import('./components/contact/index')
}]
});
This way, you can work with single Vue instance (as mentioned in the docs)

How do I pass data from PHP Laravel to Vue.js and re-render a Vue.js component when the data changes?

I'm still a bit new to Vue.js, so I may be fundamentally misunderstanding something about the reactivity of it, but basically, I want to pass some data from PHP Laravel to a Vue.js component and have the Vue.js component automatically update when that data changes.
I've seen several similar posts on SO suggest that I use props to pass the data from a Laravel Blade template to a Vue.js component. Here's an example:
How to pass a PHP variable to Vue component instance in Laravel blade?
I have this working perfectly fine, but now I'm stuck with how to get the component to update when the data dynamically changes after page-load.
Specifically, I have a report table on a particular web page, and when the user clicks certain buttons, etc., I use Ajax to call a Laravel controller action that re-runs a query in my Laravel model to get the new data for the report.
I have the data in a PHP array / JSON, and that data is being properly returned to the client-side and I have access to it in the JS, but now, what do I need to do to force the report component in Vue.js to essentially re-render based on the data I just received? Is there a way to "update props" so that Vue.js detects the change and automatically re-renders the whole report component for me?
This is where I'm stuck, and after quite a bit of research, I can't find how to do this. Thank you.
Are you using Ajax outside of the Vue component? or within it as a method?
I have an example of how I dynamically update the Vue data from within the component itself. I'm not sure how to have external JS update the Vue component directly but I feel this is a good option to look at. I'm using axios instead of Ajax but the principle is the same (Axios is included by default in most Laravel installs past 5.5).
<template>
<div>
<div id="reports">
<!-- Display data -->
{{ reports }}
</div>
<button #click="refreshReports">Refresh</button>
</div>
</template>
<script>
export default {
data() {
return {
endpoint: '/api/MY_ROUTE/'
};
},
props: {
reports: Object
},
methods: {
// Make Axios call to API endpoint
refreshReports() {
// GET version
axios.get(this.endpoint)
.then(({data}) => {
this.reports = data.data;
});
// POST version
axios.post(this.endpoint, {
KEY: 'VALUE',
}).then(({data}) => {
this.reports = data.data;
});
/*
`data.data` assumes the returned response is a JSON Resource
if it's not returning the correct data, put a `console.log(data)` and see how it's getting returned!
*/
}
}
};
</script>
Where in your routes/api.php file you have a route like this:
// GET version
Route::get('MY_ROUTE', 'ReportController#getReports');
// POST version
Route::post('MY_ROUTE', 'ReportController#getReports');
And your Controller would have some method like this:
// app/Http/Controllers/ReportController
public function getReports(Request $request) {
return Reports::all();
}
Does that make sense?
Update:
I'm not sure how to have external JS update the Vue component directly
I know you can import external JS scripts and use their functions in methods but I've never done it that way before.
Something like:
<script>
import { myFunction } from '../external.js'
export default {
methods: {
refreshReports() {
// I have no idea if this is the correct way to do it, just a guess!
this.reports = myFunction();
}
}
};
</script>

Using Vue Custom Components in Laravel Spark

I'm pretty new to using browserify etc. for js development. Also I'm new to Vue. I wanted to create additional custom components for my application. However I fail at so many points even when following different tutorials step by step.
This is What I currently got:
example.js
var MyComponent = Vue.extend({
data: function() {
return { message: 'This is a test' }
},
template: '{{ message }}'
});
Vue.component('my-component', MyComponent);
new Vue({
el: '#example'
});
app.js
require('spark-bootstrap');
require('./../example.js');
require('./components/bootstrap');
require('./backoffice/main.js');
var app = new Vue({
mixins: [require('spark')]
});
view.blade.php
<div id="example">
<my-component></my-component>
</div>
gulpfile.js
elixir(function(mix) {
mix.less('app.less')
.browserify('app.js', null, null, { paths: 'vendor/laravel/spark/resources/assets/js' })
.copy('node_modules/sweetalert/dist/sweetalert.min.js', 'public/js/sweetalert.min.js')
.copy('node_modules/sweetalert/dist/sweetalert.css', 'public/css/sweetalert.css');
});
My error
app.js:24638[Vue warn]: Unknown custom element: <my-component> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
Since example.js is a separate module, you'll need to import Vue in that file as well. I assume you've done that. You also don't want to new up a Vue instance in that file though, because you're already doing that later on. If <my-component> is inside of the spark body you already have a Vue app running.

Categories