Default Persistent Layout In Laravel + Inertia + Vite - laravel

In the previous way of setting up inertia in a laravel app, I could tweak the resolve property in the `createInertiaApp function from:
{
...,
resolve: name => import("./Pages/${name}"),
...
}
To
{
...,
resolve: name => {
const page = require("./Pages/${name}").default
if(!page.layout) {
page.layout = DefaultLayoutFile
}
},
...
}
To allow me manually pass a default layout file to be used in pages.
But with Vite becoming the default asset bundler and according to the docs, I must use a resolvePageComponent function which takes in import.meta.glob as a second argument to instruct Vite which files to bundle.
Problem here is the import gets returned from this resolvePageComponent so I cannot access the default object like I normally will from a require function.
So I have not been able to attach a default layout file to imported pages.
Has anyone been able to find a workaround for this?

Assuming you imported your default layout file like this (remember to not use # in imports anymore as only relative paths work for static analysis reasons):
import DefaultLayoutFile from './Layouts/DefaultLayoutFile.vue'
You can use the following code to get default layout working with Inertia and Vite:
resolve: (name) => {
const page = resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob("./Pages/**/*.vue")
);
page.then((module) => {
module.default.layout = module.default.layout || DefaultLayoutFile;
});
return page;
},
[UPDATE 2022-08-01]
Since this is still receiving views, I though it would be useful to show how to get the # working in imports in Vite.
Require path below your imports in vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
const path = require('path')
And then add resolve into your config below:
export default defineConfig({
resolve:{
alias:{
'#' : path.resolve(__dirname, './src')
},
},
})
Now # will point to your Laravel root and you can import components dynamically from anywhere:
For example import LayoutTop from '#/Layouts/LayoutTop.vue'
will now point to
/resources/js/Layouts/LayoutTop.vue
Remember that Vite needs the .vue extension when importing Vue SFC files.

The async await version of the accepted answer
resolve: async name => {
const page = await resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob("./Pages/**/*.vue"));
page.default.layout ??= DefaultLayoutFile;
return page;
},

This worked from me using the vite with inertia preset
import { createApp, h } from 'vue'
import { createInertiaApp } from '#inertiajs/inertia-vue3'
import { resolvePageComponent } from 'vite-plugin-laravel/inertia'
import DefaultLayout from '#/views/layouts/default.vue'
import SimpleLayout from '#/views/layouts/simple.vue'
import UserLayout from '#/views/layouts/user.vue'
createInertiaApp({
resolve: (name) => {
const page = resolvePageComponent(
name,
import.meta.glob('../views/pages/**/*.vue'),
DefaultLayout
);
page.then((module) => {
if (name.startsWith('auth.')) module.layout = SimpleLayout;
if (name.startsWith('user.')) module.layout = [DefaultLayout, UserLayout];
});
return page
},
setup({ el, app, props, plugin }) {
createApp({ render: () => h(app, props) })
.use(plugin)
.mount(el)
},
})

Related

How to define globally external plugin with inertiajs + vue in laravel?

I want to use vue-device-detector in my project. in this plugin docs it says to register with Vue.use(device) so how am i supposed to do this with inertiajs createInertiaApp.
App Versions:
Laravel: 9.44.0
Vue: 3.2.31
inertiajs/inertia: 0.11.0
inertiajs/inertia-vue3: 0.6.0
I have tried .use(device) on createApp in app.js. getting this error in console
main.ts:87 Uncaught (in promise) TypeError: Cannot set properties of undefined (setting '$device').
Project is created with laravel jetstream starter kit.
EDIT 1:
Here is my app.js code. i am don't know how i can register plugin here. (Because inertia mount the app here so i suppose global plugin also needs to be registered here!!)
import './bootstrap';
import '../css/app.css';
import { createApp, h } from 'vue';
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(ZiggyVue, Ziggy)
.mount(el);
},
});
InertiaProgress.init({ color: '#4B5563' });
PS: Thank you for reading my question, and please excuse any errors

Laravel 8 Inertia HighCharts not being imported in the app.js

I am trying to integrate Highcharts with app.js in Laravel 8 with Vue and Inertia. I am trying to figure out how to pass HighchartsVue. I am trying to pass it to the use function for the createApp. However, I can't access it in the templates.
App.js
require("./bootstrap");
// Import modules...
import { createApp, h } from "vue";
import HighCharts from ""
import {
App as InertiaApp,
plugin as InertiaPlugin,
} from "#inertiajs/inertia-vue3";
const el = document.getElementById("app");
createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
})
.mixin({ methods: { route } })
.use(InertiaPlugin)
.mount(el);
My template that tries to access the Vue Template. I haven't included the entire template here.
Vue Template
<div>
<highcharts :options="indexOptions"></highcharts>
</div>
For a global registration:
After you have installed "highcharts-vue" using:
npm install highcharts-vue
Register it globally as a plugin in your app.js with:
import HighchartsVue from 'highcharts-vue'
Next register it as a plugin in your vue object with:
Vue.use(HighchartsVue)
Please see the documentation here for more detailed instructions (and how to register it locally in the component).
After installing, your app.js would look something like this:
require("./bootstrap");
// Import modules...
import { createApp, h } from "vue";
import HighchartsVue from 'highcharts-vue'
import {
App as InertiaApp,
plugin as InertiaPlugin,
} from "#inertiajs/inertia-vue3";
Vue.use(HighchartsVue);
const el = document.getElementById("app");
createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
})
.mixin({ methods: { route } })
.use(InertiaPlugin)
.mount(el);

Can a custom vue package export pieces of a Vuex State, that could then be imported/used within a Laravel Project's Vuex State

Hey Stackoverflow community!
I've got a question regarding Vuex in a Laravel/Vue Project, that is also importing a custom Vue components library/package.
I'd like to have our package export certain pieces of Vuex state (state, mutations, getters, etc) that relate specifically to our package.
I'd like these pieces to work with our Laravel Project's Vuex Instance. The hope is that this would allow the project to use state pieces from the custom package, as well as state pieces specific to the Laravel project, in one Vuex Instance.
Is this possible or even a good approach? The package is meant to be re-usable and is not project-specific, but it would be ideal if the Laravel Project could read/manipulate/interact the package state from its own Vuex State.
Here are some relevant code snippets:
Custom Component Library - main.js
import ExampleComponent from './components/ExampleComponent.vue'
import AnotherComponent from './components/AnotherComponent.vue'
export {
ExampleComponent,
AnotherComponent
}
Laravel Project - app.js
import {ExampleComponent, AnotherComponent} from 'vue-components-library'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
filterData: {
page: 1
},
fetchResultsFlag: 0
},
mutations: {
updateFilterData: (state, filterData) => {
Object.entries(filterData).forEach(([key, value]) => {
state.filterData[key] = value
});
state.fetchResultsFlag = 1
},
incrementPage: (state) => {
state.filterData.page++
state.fetchResultsFlag = 1
},
resetFetchResultsFlag: (state) => {
state.fetchResultsFlag = 0
}
},
getters: {
filterData: state => {
return state.filterData
},
fetchResultsFlag: state => {
return state.fetchResultsFlag
},
page: state => {
return state.filterData.page
}
}
})
const app = new Vue({
el: '#app',
store: store
});
Any help or insight is greatly appreciated!
Thanks.

Using jetstream inertia with laravel modules

I would like to use nwidart/laravel-modules and inertia together. The problem I'm having is that my Index.vue that is in my Users module isn't showing up and there is no errors.
When I have this line of code in my app.js
resolveComponent: (name) => require(`../../Modules/Users/Resources/assets/Pages/${name}`).default
then my Index.vue shows up, but when I try to loop through it so that I can make it dynamic nothing shows up and there is no errors
This is what I have in my app.js at the moment
require('./bootstrap');
require('moment');
import Vue from 'vue';
import { InertiaApp } from '#inertiajs/inertia-vue';
import { InertiaForm } from 'laravel-jetstream';
import PortalVue from 'portal-vue';
Vue.mixin({ methods: { route } });
Vue.use(InertiaApp);
Vue.use(InertiaForm);
Vue.use(PortalVue);
const app = document.getElementById('app');
const modulesJson = require("../../modules_statuses.json");
var modulesObject = Object.keys(modulesJson);
new Vue({
render: (h) =>
h(InertiaApp, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: (name) => {
// LOOP IS HERE
for(let i = 0; i < modulesObject.length; i++){
require('../../Modules/'+modulesObject[i]+'/Resources/assets/Pages/'+name).default
}
}
},
}),
}).$mount(app);
Write the path of a file in your module when you want to run it through inertia. Then put a condition in the app.js file that if it is sent through the module, this code will be executed, otherwise it will return to the desired pages in the folder pages.
Like the following code:
Route::get('test',function () {
return \Inertia\Inertia::render('modules/Faq/resources/js/Pages/Admin/Index');
});
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => name.toString().indexOf('modules') > -1 ? require(`../../${name}.vue`) : require(`./Pages/${name}.vue`),
});

Vue <template> in a .vue file with Lodash

In my .vue file within my template section I have:
<a v-bind:href="'javascript:artist(\'' + _.escape(artist) + '\')'">
which is using the Lodash function _.escape. This generates a string of errors the first of which is:
[Vue warn]: Property or method "_" is not defined on the instance but referenced during
render.
However in the same file in the script section of the component I am happily and successfully using a range of Lodash functions.
This is a Laravel app and in my app.js file I have this code:
require('./bootstrap');
window.Vue = require('vue');
import VueRouter from 'vue-router';
window.Vue.use(VueRouter);
import lodash from 'lodash';
Object.defineProperty(Vue.prototype, '$lodash', { value: lodash });
import SearchHome from './components/search.vue';
const routes = [
{
path: '/',
components: {
searchHome: SearchHome
}
},
]
const router = new VueRouter({ routes })
const app = new Vue({ router }).$mount('#app')
Can anyone please help me?
Try to use a computed value instead. This will improve readability.
Avoid complex operation in a binding.
<a :href="artistLink">
And in the script
import _ from 'lodash'
export default {
computed: {
artistLink () {
return 'javascript:artist(\'' + _.escape(this.artist) + '\')'
}
}
}

Resources