How to init Vuejs + VueRouter on certain pages within Laravel - laravel

To explain it better I am creating an app with Laravel back-end (for learning purposes) and I am trying too hook lots of stuff. But I want to create only one or two of the pages to run the vue/vue-router to display certain components. Like multi-page website with few single-page apps within it.
I cut it rough
<div id="{{ (Route::current()->getName() == 'vue-page1') ? 'app' : '' }}">
#yield('content')
</div>
but this is no solution I tried to limit the pages after that with JS using
if (!document.getElementById("app"))
doesn't get it, Vue is still initiated. I want to keep the current structure, just to stop it from initialization on pages where it shouldn't.

Based on the code that you posted try to build the options object beforehand than to pass it to the new Vue instance. Like that:
let options = {
el: '#app',
name: 'app',
render: app => app(App),
data: {
a: 1
},
created: function () {
// `this` points to the vm instance
console.log('a is: ' + this.a)
},
mounted() {
console.log('done');
auth.check()
}
}
if(document.getElementById("app-with-routes"))//yaah we have app with routes
{
window.VueRouter = require('vue-router');
let router = new VueRouter({
routes: [
{
path: '/',
name: 'home',
component: Vue.component('home', require('./components/Home.vue'))
},
// .... all the routes you need
]});
options['router'] = router
}
const app = new Vue(options);
That way you will be able to use all vue sweet parts without having to deal with the router.

Have you tried including your vue.js only in pages that you need to?
#section('styles')
{{ (Route::current()->getName() == 'vue-page1') ? '<script src="https://unpkg.com/vue/dist/vue.js"></script>' : '' }}
#endsection

Related

Laravel vue.js 2 response view won't output parameters

I am new to vue.js and recently been assigned a project to learn it and refactor old code to vue.js like some of the existing pages already have been and I am having some issues. vue2
My question:
I have a get request with a controller that has a response something like this:
return Response::view('somebladefile', ['title' => 'some_title']);
Within the blade file I include a js file which will be responsible for vue
<script src="{{ cdnMix('somefile.js') }}"></script>
somefile.js contents:
const IndexPage = Vue.component('indexpage',require('./somepath/IndexPage.vue').default);
window.indexPageInstance = new IndexPage().$mount('#vuecontainerid');
So now within IndexPage.vue i would like to access variable 'title' that I passed with the response to my blade file originally. What would be the best way one would go about it? Tried few ways I found on YT/Google but without success, this is my code currently, any pointers would be appreciated, thanks!
<template>
<HeaderComponent></HeaderComponent>
</template>
<script>
const HeaderComponent = require('./somepath/HeaderComponent.vue').default;
export default {
name: 'indexpage',
props: ['data'],
components: {
'HeaderComponent': HeaderComponent,
},
data: function() {
return {
// why doesn't it work!!: '',
}
},
mounted: function() {
console.log(this.data);
}
}
</script>
Vue works, but I can't seem to be able to access 'title' variable.
Also I would like to be able to access 'title' within other components like the HeaderComponent I have within indexpage

Create global method vue on app.js laravel

I want create global method to translate message using Laravel-JS-Localization
But when i call the method using vue mustache got an error like this:
Property or method "trans" is not defined on the instance but referenced during render.
Make sure that this property is reactive.
Here my laravel app.js code:
require('./bootstrap');
window.Vue = require('vue');
Vue.component('dashboard', require('./components/Dashboard').default);
const app = new Vue({
el: '#vue',
methods: {
trans: function (key) {
return Lang.get(key);
},
},
});
Dashboard.vue code :
<template>
<p>{{ trans('common.welcome') }}</p>
</template>
<script>
data () {
return {
name: '',
}
},
</script>
dashboard.blade.php code :
..........
<div class="col-9" id="vue">
<dashboard></dashboard>
</div> <!--c end col-8 -->
..........
I would probably go with creating a Plugin. For example
Vue.use({
install (Vue) {
Vue.prototype.$trans = Lang.get
}
})
Adding this to your app.js code before creating any components or new Vue({...}) will mean all your components will have access to the $trans method.
Alternatively, you can create a Global Mixin but these aren't strongly recommended.
Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components
Vue.mixin({
methods: {
trans (key) {
return Lang.get(key)
}
}
})

NativeScript vue, vuex and urlhandler

Edit
I'm using https://github.com/hypery2k/nativescript-urlhandler to open a deep link within my app - using NativeScript vue, and vuex. It seems that in order to get at the methods needed to do routing [$navigateTo etc] this plugin needs to be set up slightly differently from the examples given in docs.
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
import { handleOpenURL } from 'nativescript-urlhandler';
new Vue({
mounted() {
handleOpenURL( (appURL) => {
console.log(appURL)
// Settings is the variable that equals the component - in this case settings.
this.$navigateTo(Settings);
});
},
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();
handleOpenURL needs to be called within Mounted - then you can parse out the appURL and reference the page (component) that you wish to navigate to. I have been advised against calling handleOpenURL from within router - but I'm not sure why, and it works without error - and I have access to the methods for routing... so if anyone knows if this is a bad idea - please let me know :) Thanks!
All the stuff below that I wrote before has probably confused things - I'm referencing components within my vuex store to make them easily available from the router.
This is based on a solution by https://github.com/Spacarar - it can be found here: https://github.com/geodav-tech/vue-nativescript-router-example. It's a great solution because you don't have to include every single component within each component to use in navigation - it gives an almost vue router like experience.
I'm using https://github.com/hypery2k/nativescript-urlhandler to open a deep link within my app - however, I'm having problems opening the link.
In my app.js file, I have the following:
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
....
import { handleOpenURL } from 'nativescript-urlhandler';
import ccStore from './store/store';
handleOpenURL(function(appURL) {
// I have hardwired 'Settings' in for testing purposes - but this would be the appURL
ccStore.dispatch('openAppURL', 'Settings');
});
....
new Vue({
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();
I'm storing the route state within vuex, and have various methods which work (clicking on a link loads the component). However, handleOpenURL exists outside of vue... so I've had to access vuex directly from within the handleOpenURL method. I've created an action specifically for this case - openAppURL.. it does exactly the same thing as my other methods (although I've consolidated it).
When clicking on an app link, I am NOT taken to the page within the app. I have put a console log within openAppURL and can see it is being called, and the correct route object is returned... it just doesn't open the page. The SetTimeOut is used because nextTick isn't available from within vuex.
I am at a loss on how to get the page to appear...
const ccStore = new Vuex.Store({
state: {
user: {
authToken: null,
refreshToken: null,
},
routes: [
{
name: "Home",
component: Home
},
{
name: "Log In",
component: Login
},
...
],
currentRoute: {
//INITIALIZE THIS WITH YOUR HOME PAGE
name: "Home",
component: Home //COMPONENT
},
history: [],
},
mutations: {
navigateTo(state, newRoute, options) {
state.history.push({
route: newRoute,
options
});
},
},
actions: {
openAppURL({state, commit}, routeName ) {
const URL = state.routes[state.routes.map( (route) => {
return route.name;
}).indexOf(routeName)];
return setTimeout(() => {
commit('navigateTo', URL, { animated: false, clearHistory: true });
}, 10000);
},
....
}
etc....
I have been advised to post my findings as the answer and mark it as correct. In order to use nativescript-urlhandler with vue, you must initialise the handler from within vue's mounted life cycle hook. Please see above for greater detail.
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
import Settings from "~/components/Settings";
import { handleOpenURL } from 'nativescript-urlhandler';
new Vue({
mounted() {
handleOpenURL( (appURL) => {
console.log(appURL) // here you can get your appURL path etc and map it to a component... eg path === 'Settings. In this example I've just hardwired it in.
this.$navigateTo(Settings);
});
},
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();

Laravel compact in vue

I want to know how can I pass variables to vue component in laravel?
When we work with blade we can pass variables like:
$now = Carbon::now();
return view('xxxxxxxx', compact('now');
That way I can use $now in xxxxxxxx blade file. But what about vue components? we usually return data by json for components and with axios route we get that info no way to specify such data for exact component of us?
What if I want to use $now = Carbon::now(); in single.vue component?
How can I make that happen?
Update
Here is what I want to do with timing as carbon cannot be used (based on comments) I want to use moment.js
Logic
Let users bid if project deadline hasn't arrived
Don't let users bid if project deadline has arrived
template
<template v-if="`${project.deadline | timeAgo}`">
pssed (will be replaced by button is just for test)
</template>
<template v-else>
still have time (will be replaced by button is just for test)
</template>
script
var moment = require('moment');
export default {
data(){
return {
project : '',
}
},
mounted: function() {
// I found this code by google not sure if is currect!
Vue.filter('timeAgo', function(value){
return moment(value) >= fromNow()
});
},
}
Based on my code above here is the results
Try this:
This is my routes, simply I just render a view with some pre-defined variables
Route::get('/', function () {
return view('welcome', [
'now' => Carbon::now(),
'deadline' => Carbon::now()->addHours(2)
]);
});
And this is my view file. Here I have custom element named: example-component. And this is how I passed PHP variables to Vue component using props.
And pass your data to window like so:
<script>window.data = #json(compact('now', 'deadline'))</script>
And this is my Vue component file:
<template>
<h1>
<span v-if="isPassed">This job is passed</span>
<span v-else>You have to finish this job</span>
{{ parsedDeadline | timeFromX(parsedNow) }}
</h1>
</template>
<script>
const moment = require('moment');
export default {
props: {
now: {
type: String,
default: () => window.data.now.date // since `now` is an object, you need to access the `date` property to get plain string date that moment can parse
},
deadline: {
type: String,
default: () => window.data.deadline.date // same as above
}
},
computed: {
parsedNow () {
return moment(this.now)
},
parsedDeadline () {
return moment(this.deadline)
},
isPassed () {
return this.parsedNow.isAfter(this.parsedDeadline)
}
}
}
</script>
Here's the documentation about computed and filters. You may NEVER add a filter in a mounted function since it may leads to memory leak. Here's how I add my filter. In your app.js (assumed you're using default Laravel Vue preset)
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('example-component', require('./components/ExampleComponent.vue'));
Vue.filter('timeFromX', (a, b) => a.from(b)); // TADAAA...!!!
const app = new Vue({
el: '#app'
});
UPDATE
If you want to try this, you may edit the routes/web.php and change the deadline value:
Route::get('/', function () {
return view('welcome', [
'now' => Carbon::now(),
'deadline' => Carbon::now()->subHours(2), // Passed 2 hours ago
// 'deadline' => Carbon::now()->addHours(2), // In 2 hours
]);
});
Checkout the Carbon docs here about addition and subtraction.
UPDATE II
If you got error in app.js from the code above, maybe your transpiler doesn't know about arrow-parens.
// Looks like arrow-parens not working, see code below
// Vue.filter('timeFromX', (a, b) => a.from(b)); // TADAAA...!!!
// Change it to this ???
Vue.filter('timeFromX', function (a, b) {
return a.from(b);
});

How can I preload data for vue.js in Laravel Spark?

According to the docs and examples, I have perfectly working code that functions great:
Vue.component('admin-competitions-index', {
data: function() {
return {
competitions: []
}
},
mounted() {
this.$http.get('/api/admin/competitions')
.then(response => {
this.competitions = response.data;
});
},
methods: {
/**
* Toggle whether a competition is published or not.
*/
togglePublished(competition) {
Spark.patch(`/api/admin/competitions/togglePublished/${competition.id}`, this.togglePublishedForm)
.then(response => {
competition.is_published = response;
});
}
}
});
However, I'd like to change this code to save the extra request that is made on page load. I don't see a convention anywhere in Laravel or Spark where this is done. I'm guessing that all I need to do is set a JS variable but I'm not sure where it would be proper to do so.
I also understand that this kind of defeats the point of using vue for asynchronous loading, but nevertheless I would like to learn this. I think it will become more useful if I were to use vue for my #show restful requests where even if I wanted everything to load asynchronously I would at the very least have to supply vue with the competition ID that I want loaded.
This works out of the box:
#section('scripts')
<script>
var competition = {!! $competition !!};
</script>
#endsection

Resources