How to use Vue on a site with multiple pages? - php

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

Related

How can I Integrate Vue into a legacy PHP app and gain access to specific Vue components

I have a server-side-rendered legacy PHP 5.6 web app that I want to modernise using VueJS
Within the project structure I have set up a new Vue project and set up Vue Router and Webpack with an entry point of main.js
Main.js
Vue.config.productionTip = false
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import router from './router/router'
new Vue({
render: h => h(App),
router
}).$mount('#app')
I am currently inserting VueJS into the legacy app by inserting this code at the bottom of a php file:
<div id="app">
</div>
<script src="http://localhost:8080/js/main.js"></script>
The url of this page is https://www.example.com/user_portal/employee_holiday_info.php#/
However Vue Router recognises it as / because it is the point of entry.
At the moment I have to insert the above code on every PHP file that I want to use VueJS on. Is there a better way of doing this?
If I insert the above code at the bottom of say, index.php then Vue Router would also recognise that page as /. In which case both urls:
https://www.example.com/user_portal/employee_holiday_info.php#/
https://www.example.com/user_portal/index.phpwould render the same Vue component, HelloWorld.
Vue Router
import HelloWorld from "../components/HelloWorld";
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
name: 'hello-world',
component: HelloWorld,
}
]
});
export default router;
How can I set this up so I can on multiple Vue components throughout a single php file and share data between them using Vuex.

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 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)

Best practice to locally register Vue components in Blade template (Laravel)

I'm building a MPA project with Laravel + Vue where I currently register all components globally, so my app.js looks like this:
Vue.component(...)
Vue.component(...)
...
new Vue({
el: '#app'
});
I am thinking it might be better if we register just some global components and then register local components for a specific blade view.
Let's say in the js/components folder I have a global folder contains all global components and a auth/login folder contains all components for the login page.
And for the login blade view:
#extends(layouts.app)
#section('content')
<div id="login-page">
</div>
#endsection
What would be the best practice to structure files/register Vue components for this specific blade view?
Pointers will be appreciated.
Most of the time I arrange my components on a functionality basis. One folder per functionality in js/components (and sub-folders if the view is complex). Then I have a components-loader.js file where I keep track of all my global components. The components-loader.js only contains the root components and the partials imported in vue, basically creating a chain.
In the components-loader.js file I load the components in a way that Webpack can use chunking, so each component have it's own chunk-x.js file. And simply require the loader in your app.js, this way the app.js can be a lot smaller.
components-loader.js:
Vue.component('example-component', function (resolve) {
require(['./exampleFunctionality/exampleComponent'], resolve);
});
app.js:
require("./components/components-loader");
Create the folder /public/chunks and in webpack.mix.js put at the end:
.webpackConfig({
output: {
chunkFilename: `chunks/[name]${mix.config.inProduction ? '[chunkhash].chunk.js' : '.chunk.js'}`,
publicPath: '/',
},
});
Now if you run npm run dev/prod one folder of components will be one chunk, and webpack only pulls the files to the browser when it's used. It has a small delay, but worth it in my opinion.
Then in your blade view just use the root component.
#extends(layouts.app)
#section('content')
<example-component :prop1="asdasd" :prop2="basdasd"></example-component>
#endsection
This might not be the best way but it works nicely for me.

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