Closed ycs77 closed 2 years ago
I've also hit this. I have some of my optional routes defined inside a package, and the javascript component ends up being published out to resources/vendor/MyPackage/js/Pages/MyPackagePage.js
In the package controller, I can't just do return Inertia::render('MyPackagePage');
because that would look inside resources/js/Pages
, I would need to do something like:
return Inertia::render('../vendor/MyPackage/js/Pages/MyPackagePage');`
which isn't ideal, and doesn't even seem to work.
I would love this feature too, however i found my way around this which i gonna post here in hope it might help you?
for production i publish the resources to the app's resource folder:
/*
* to publish assets one can run:
* php artisan vendor:publish --tag=web --force
* or use Laravel Mix to copy the folder to public repo of core.
*/
$this->publishes([
__DIR__ . '/public/img' => public_path('img'),
__DIR__ . '/resources/js' => resource_path('js'),
__DIR__ . '/resources/sass' => resource_path('sass'),
$this->getPackageJsonFile() => base_path('package.json'),
$this->getPackageLockJsonFile() => base_path('package-lock.json'),
], 'web');
during development i have npm run watch
running and refering the pages in my package alike (note the copyDirectoryWatched
)
if (! mix.inProduction()) {
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.copyDirectoryWatched('packages/seatplus/web/src/resources/js/Pages/Configuration', 'resources/js/Pages/Configuration')
.webpackConfig({
output : {chunkFilename: 'js/[name].js?id=[chunkhash]'},
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.esm.js',
'@' : path.resolve('resources/js'),
},
},
})
}
Like this you are able to work out your package as you would do it in the main application.
Contrary to Blade, Inertia doesn't look for views, they're just strings.
You have full control over your own app.js
, which is where the views can be resolved as you see fit.
Quick example:
return Inertia::render('my-package::Home/Index');
new Vue({
render: h => h(InertiaApp, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: name => {
if (name.startsWith('my-package::')) {
return require(`../../vendor/my/package/resources/js/Pages/${name.substr(12)}`).default
}
return require(`./Pages/${name}`).default
}
},
}),
}).$mount(app)
Inertia doesn't impose any conventions regarding your frontend code, so I'm not sure what can be done to streamline this.
The only problem with that is that the app.js
would need to be aware of all the possible packages in order to resolve the paths, rather than the package being able inject themselves.
The suggestion from OP seems better in terms of isolation / injection, you would tell inertia that you have views in a package folder, and then when you call Inertia::render('my-package::Component')
, it could internally check the map of namespaces and convert the component argument before passing it down to the javascript.
This would allow the javascript resolveComponent
function to remain unaware of the existence of any packages.
I've spent a bit longer on this today and realised @sebastiandedeyne is right. You need the requires to have at least the partial path to allow webpack to statically analyse the lazy load possibilities, so doing it inside the componentResolver makes a lot more sense.
This is may solution to call and component from modules and packages
app.js The structure to call is: Params to from Module: Type[Module]/PathToResourcesAssetsJsPages/ViewName.vue Params to from Package: Type[Package]/PathToVendorFolder/VendorName/PackageName/PathToResourcesAssetsJsPages/ViewName.vue
Call from modules using https://github.com/nWidart/laravel-modules
Module file structure modulo it's default use the package when create a new module
Call from package
Packges file structure are usually
webpack.mix.js
Just wondering at this point a guide about creating packages with Inertia would be a nice addition :)
For future reference here is my approach. Built a package as a standalone dashboard using inertia, where target app does not has inertia or laravel mix.
my current implementation for laravel package
resources/js/app.js
new Vue({
render: (h) =>
h(InertiaApp, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: (name) => {
let splited = name.split('::')
if (splited.length == 1) return require(`./Pages/${name}`).default
let [first, page] = splited
let [author, plugin] = first.split('/')
// '~': path.resolve('vendor')
return require(`~/${author}/${plugin}/resources/js/Pages/${page}`).default
},
},
}),
}).$mount(app);
Author/PackageName/Controller/DashboardController.php
class DashboardController extends Controller
{
public function index()
{
return inertia('author/package-name::Dashboard');
}
}
i hope there is a better and proper way to implement inertiajs return view on laravel package.
@vortechron this looks very promising i am likely ditching my approach in favors of yours
i gave @vortechron 's solution a try and i wasn't satisfied. running dev/prod with npm took way to long.
I'm also interested on this feature. Has anyone found a workaround for this?
I'm also interested on this feature. Has anyone found a workaround for this?
Actually work with modules package + inertiajs + vue/react + bootstrap/tailwind
@yashiroiori can you explain how did you manage to setup the packages, so that views are loaded with Inertia?
@yashiroiori can you explain how did you manage to setup the packages, so that views are loaded with Inertia?
+1
I think this can be done using symlink
and I had it working on my local but there are few things need to be addressed.
This is an example directory
.
├── app
├── resources/
│ └── js/
│ ├── app.js
│ └── Pages/
└── vendor/
└── my-package/
├── config/
├── src/
│ └── ServiceProvider.php
└── js/
├── ComponentA.vue
└── ComponentB.vue
In your package ServiceProvider
, register the component hint path
Inertia::loadComponentsFrom(__DIR__.'/../js', 'my-package');
Then have artisan command similar to storage:link
to generate the symlink from vendor/my-package/js
to resources/js/Pages/vendor
.
├── app
├── resources/
│ └── js/
│ ├── app.js
│ └── Pages/
│ └── vendor/
│ └── my-package -> symlink to vendor/my-package/js/
└── vendor/
└── my-package/
├── config/
├── src/
│ └── ServiceProvider.php
└── js/
├── ComponentA.vue
└── ComponentB.vue
In your controller you can use something like this
Inertia::render('my-package::ComponentA');
It will replace the the component from my-package::ComponentA
to vendor/my-package/ComponentA
and you don't have to update your app.js
The command can be added to composer post-autoload-dump
script to generate the symlink
automatically. However there is a case when assets are published to resources
folder, the symlink need to be updated also. The good news is there is VendorTagPublished
event for this but the bad news is it only available since Laravel 8. I can create a PR for this when I have time to address this issue.
How do you guys think about this approach?
OK. I think I got the answer. Thanks all for the ideas, inspired me to create a plugin.
Warning: This plugin status is experimental, don't use it for the production application.
The plugin page loader for Inertia.js, powered by unplugin, supports Vite and Laravel Mix.
Define the namespace mapping for plugins pages directory, also can be extract namespace from the npm / composer package:
// webpack.mix.js
const InertiaPlugin = require('inertia-plugin/webpack')
mix
.webpackConfig({
plugins: [
InertiaPlugin({
namespaces: ({ npm, composer }) => [
// define namespace mapping:
{ 'my-package-1': 'node_modules/my-plugin1/src/Pages' },
// load namespace from npm package:
npm('my-plugin2'),
// load namespace from composer package:
composer('ycs77/my-php-package'),
],
}),
],
})
The npm package like node_modules/my-plugin2/package.json
:
{
"name": "my-plugin2",
"inertia": {
"my-npm-package": "src/other-pages"
}
}
The composer package like vendor/ycs77/my-php-package/composer.json
:
{
"name": "ycs77/my-php-package",
"extra": {
"inertia": {
"my-php-package": "resources/js/Pages"
}
}
}
Will can be used namespace prefix get the view:
Inertia::render('my-package-1::Page3');
Inertia::render('my-npm-package::Page222');
Inertia::render('my-php-package::PhpPackagePage');
For full usage see the docs.
Try it~ 😊
my current implementation for laravel package
resources/js/app.js
new Vue({ render: (h) => h(InertiaApp, { props: { initialPage: JSON.parse(app.dataset.page), resolveComponent: (name) => { let splited = name.split('::') if (splited.length == 1) return require(`./Pages/${name}`).default let [first, page] = splited let [author, plugin] = first.split('/') // '~': path.resolve('vendor') return require(`~/${author}/${plugin}/resources/js/Pages/${page}`).default }, }, }), }).$mount(app);
Author/PackageName/Controller/DashboardController.php
class DashboardController extends Controller { public function index() { return inertia('author/package-name::Dashboard'); } }
i hope there is a better and proper way to implement inertiajs return view on laravel package.
when i try to use this. i have an error javascript heap out of memory looks like it will compile all the package.
If I build a Laravel package, I hope I can load Inertia views into the package.
Load Inertia views example:
Usage Inertia views example:
Can it be achieved?
Updated on 2022/07/03:
Thanks all for the ideas, inspired me to create the Inertia Plugin to resolve this issue.