I'm pretty new to Inertia (I have enough experience with Laravel), so I'm writting a toy SPA application. I learn that I must use the <Link ...> component instead of <a ...> to get the SPA behaivour. Problem is that I have to import the Link component on every other component that'll use links.
So, if I have a Page, I should do something like this:
<template>
...
<Link href="/about" class="...">
About Page
</Link>
...
</template>
<script>
import { Head, Link } from "#inertiajs/inertia-vue3";
export default {
components: {
Head,
Link,
},
...
};
</script>
And this works, but I think it's quite unefficient, boresome and so to have to import the Head and Link components for every page, after all a Link is the most common element on a page other than plan text.
Here https://inertiajs.com/releases/inertia-vue3-0.5.0-2021-07-13 in the documentation says you can register Link and Head components globally, so my app.js code looks like:
require("./bootstrap");
import { createApp, h } from "vue";
import { createInertiaApp } from "#inertiajs/inertia-vue3";
import { InertiaProgress } from "#inertiajs/progress";
import { Head, Link } from "#inertiajs/inertia-vue3";
const appName =
window.document.getElementsByTagName("title")[0]?.innerText || "Laravel";
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
createApp({ render: () => h(app, props) })
.use(plugin)
.component("InertiaHead", Head)
.component("InertiaLink", Link)
.mixin({ methods: { route } })
.mount(el);
},
});
As the documentation says, but this does nothing. When I comment the import ... and components section on my page component. It doesn't throw an error, but it doesn't display anything, not even the text.
Any idea?
You have registered your components as "InertiaLink" and "InertiaHead". If you do this, you must also name them that way in the vue files.
app.js
vue file
Related
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
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)
},
})
With a newly created Laravel with Inertia and Vue project, I have an error that it runs code inside my vue components twice.
For a simple test demo, I have a Test.vue, with this code
<template>
<div>
TEST
</div>
</template>
<script>
export default {
name: "Test",
mounted() {
console.log('test')
}
}
</script>
<style scoped>
</style>
Route in web.php:
Route::get('/test', array(App\Http\Controllers\DashboardController::class, 'test'));
In controller:
public function test(): Response
{
return Inertia::render('Test');
}
When I go to the route /test, it echoes out 'test' twice on mounted in my console. In more advanced components which calls APIs and such, also calls them twice.
I suspect maybe my project is set up wrongly, in app.blade.php or app.js but cannot figure it out.
I followed the guides on https://inertiajs.com/server-side-setup and https://inertiajs.com/client-side-setup to set this up.
My source code is here: https://github.com/ekstremedia/laravel-inertia
Edit: It seems to only echo out twice in the first component I load. If I in that component link to another component, and go there, it doesn't load that twice.
I found that this is solved by modifying your app.js file in your resources directory thus:
import Vue from 'vue'
import { App as InertiaApp, plugin as InertiaPlugin } from '#inertiajs/inertia-vue'
Vue.use(InertiaPlugin)
new Vue({
render: (h) =>
h(InertiaApp, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
},
}),
}).$mount(app);
Your problem seems to have been in how you initialized your inertia app.
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);
I'm trying to get up and rolling with Vue Native, and I'm running into the same error whenever I attempt to navigate beyond the initial screen
Warning: React.createElement: type is invalid -- expected a string
(for built-in components) or a class/function (for composite
components) but got: undefined. You likely forgot to export your
component from the file it's defined in, or you might have mixed up
default and named imports.
Check the render method of ReactVueComponent.
in ReactVueComponent (at SceneView.js:17)
in SceneView (at CardStack.js:466)
in RCTView (at View.js:60)
in View (at createAnimatedComponent.js:154)
in AnimatedComponent (at Card.js:12)
When searching my dir for ReactVueComponent, it doesn't exist, nor does SceneView.js, nor does RCTCView, etc. My guess is that's because they are generated with the code compiles?
My router, index.vue is set up as follows
<template>
<root>
<app-navigation></app-navigation>
</root>
</template>
<script>
import React from "react";
import { StackNavigator, navigationService } from "vue-native-router";
import { Root } from "native-base";
import WelcomeScreen from "./screen/WelcomeScreen.vue";
import HomeScreen from "./screen/home.vue";
const AppNavigation = StackNavigator(
{
Welcome: { screen: WelcomeScreen },
Home: { screen: HomeScreen }
},
{
initialRouteName: "Welcome",
headerMode: "none"
}
);
export default {
components: { Root, AppNavigation }
};
</script>
My WelcomeScreen component(this loads correctly. The button, on push, throws the error)
<template>
<nb-content padder>
<nb-form>
<view :style="{marginTop:300}">
<nb-button block :on-press="login">
<nb-text>Login</nb-text>
</nb-button>
</view>
</nb-content>
</template>
<script>
import { Dimensions, Platform, AsyncStorage } from "react-native";
import { NavigationActions } from "vue-native-router";
export default {
props: {
navigation: {
type: Object
}
},
methods: {
login() {
this.navigation.navigate("Home");
}
}
};
</script>
The HomeScreen component, which fails to render:
<template>
<nb-container :style="{flex:1, backgroundColor: '#fff'}">
<nb-header>
<nb-body>
<nb-title>title</nb-title>
</nb-body>
</nb-header>
<nb-content>
<nb-list>
<li>thing 1</li>
<li>thing 2</li>
<li>thing 3</li>
</nb-list>
</nb-content>
</nb-container>
</template>
<script>
import React from "react";
import { Dimensions } from "react-native";
const SCREEN_WIDTH = Dimensions.get("window").width;
export default {
props: {
navigation: Object
}
};
</script>
Any tips on this would be much appreciated. Not much out there on Vue Native yet, and I've tried to follow the few examples I've seen to the best of my ability. Double and triple-checked my dependencies and they all seem to be in place.
Seems like you are using <li> tags which are not supported. If you check native base docs. The correct tag to be used within nb-list is nb-list-item. http://docs.nativebase.io/Components.html#list-def-headref