How to set up Alpine.js 3 with mix - node-modules

I am having trouble getting started with Alpine.js v3 in a new project. This is my package.json and Alpine setup:
"devDependencies": {
"laravel-mix": "^6.0.19"
},
"dependencies": {
"alpinejs": "^3.0.6"
}
import Alpine from 'alpinejs'
window.Alpine = Alpine
window.Alpine.start()
Alpine.data('demo', () => ({
open: false,
toggle() {
this.open = !this.open
}
}))
driving this markup:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo</title>
<script src="assets/app.js"></script>
</head>
<body>
<div x-data="demo">
<button #click="toggle">Expand</button>
<div x-show="open">Content...</div>
</div>
</body>
</html>
and I build my javascript using laravel-mix:
// webpack.mix.js
let mix = require('laravel-mix');
mix
.setPublicPath('public')
.js('src/app.js', 'public/assets');
The javascript build fine and the page loads without errors, but when I click the toggle button, the content expands as expected, but I get the following error in the browser console and the content won't hide on subsequent clicks.
Uncaught (in promise) TypeError: i is not a function app.js:2282:28
If I defer my script or move it to the bottom, the page is broken on load and I get the following:
Uncaught TypeError: undefined is not a non-null object app.js:1995:12
Can anyone help me fix my setup?

Most likely you just forget to add defer attribute to script tag
<script defer src="assets/app.js"></script>
But also as documentation says
If you imported Alpine into a bundle, you have to make sure you are registering any extension code IN BETWEEN when you import the Alpine global object, and when you initialize Alpine by calling Alpine.start().
So change your script to
import Alpine from 'alpinejs';
Alpine.data('demo', () => ({
open: false,
toggle() {
this.open = !this.open
}
}));
window.Alpine = Alpine;
// should be last
Alpine.start();
Note: if there are some errors like assignment to undeclared variable data you may change your import to import Alpine from 'alpinejs/dist/module.cjs';

Related

how to reuse components and pass props in vue 3 and vitejs and laravel 9?

im using laravel 9, vitjs and vue3 to create an app.
im new in vue3 and vitjs and i started to create vue components and use them
my codes to register component in app.js:
import { createApp } from "vue"
import testcomponent from './components/testcomponent.vue';
createApp(testcomponent).mount('#app');
ok, I have <div id="app"></div> in my laravel blade file which my component load in it and displays fine.
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
#vite(['resources/sass/app.scss'])
</head>
<body>
<div id="app"></div>
#vite(['resources/js/app.js'])
</body>
But what should I do if I want to pass props to my component?
why we cant use component like as vue2 (tags) like this: <testcomponent></testcomponent>
Is this way because of vue3 or vitejs?
STEP 1: Register Vue app by following way
import { createApp, defineAsyncComponent } from "vue";
const app = createApp({});
//register component globally
const testcomponent= defineAsyncComponent(() => import('./components/testcomponent.vue'));
app.component('test-component', testcomponent);
app.mount('#app');
different ways to import component:
1st Way:
import testcomponent from './components/testcomponent.vue';
2nd Way:
const testcomponent = import("./components/testcomponent.vue")
3rd Way:
const testcomponent = defineAsyncComponent(() => import('./components/testcomponent.vue'))
STEP 2: call a component using tags in blade.
<div id="app">
<test-component :prop-name="{{ json_encode($Obj) }}"></test-component>
</div>
Plz try it.

Vue doesn't render anymore components in production after moving from webpack to Vite

The vue method app.component() called on an app instance (i.e., obtained through createApp()) doesn't work anymore in production as expected (as does in the dev environment).
For example, the following code works in the dev environment but not in production:
app.js
import Foo from './Foo.vue';
const app = createApp({});
app.component('foo', Foo)
.mount('#app');
app.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
#vite('resources/js/app.js')
</head>
<body>
<div id="app">
<foo />
</div>
</body>
</html>
Vue replaces the above div#app with <!---->.
However, the following code works fine in both:
app.js
import Foo from './Foo.vue';
createApp(Foo).mount('#app');
app.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
#vite('resources/js/app.js')
</head>
<body>
<div id="app"></div>
</body>
</html>
Now, the problem is that I need the first js code working because I'm passing data from Blade to Vue components, or I would have to make a porting of multiple Blade files into Vue.
This is my vite.config.js:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/js/app.js',
],
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
});

Vite - Rollup node_modules not defined as modules

I have a Webpack project which I am migrating to Vite. I have the dev server working, and now I am trying to get the build to work. I have run into an issue where a number of my dependencies cannot be imported as modules because they must be global. This includes Backbone, jQuery, moment.js, and DevExtreme. (I will be slowly getting rid of these dependencies, but for now, there is a large code base which is based on them)
I first tried to have them loaded as modules and manually add them to the global context, but that failed because when importing you can't manage the order in which the files are loaded. Backbone and DevExtreme require jQuery to be loaded in first. If it isn't, the page fails to load.
So I have a list of node_modules referenced directly from my html entry points.
Here is a simplified example of what I currently have.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1"
/>
<title>rollup test</title>
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
</head>
<body>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
oldMain.js
$(function() {
$("body").append("Here is some text");
});
main.ts
import './oldMain.js';
vite.config.ts
import path from "path";
import { defineConfig } from "vite";
export default defineConfig({
build: {
rollupOptions: {
input: {
index: path.resolve(__dirname, "index.html"),
},
}
}
});
package.json:
{
"name": "issue-example",
"version": "1.0.0",
"main": "index.html",
"scripts": {
"build": "vue-tsc --noEmit && vite build"
},
"dependencies": {
"jquery": "^3.6.0",
"vue": "^3.2.33",
},
"devDependencies": {
"typescript": "^4.6.3",
"vite": "^2.9.5",
"vue-tsc": "^0.34.10",
}
}
At the moment, the only way forward that I can see is to write a rollup plugin that extracts the list of non-module scripts from the html file, gets the target files from their paths, copies them to the dist/assets directory, and changes the src paths in the html to the new location.
Is there a better way? A plugin that already exists that knows how to do this? Is there a different way to include these dependencies so they will be globally available?
The solution I ended up going with is:
I use handlebars to create the index.html file the necessary scripts.
Have a .ts file which decides what scripts need to be in the index.html file. It checks whether the current build is dev or prod and puts the relevant path in for each script.
My index.html looks like:
<html>
<head>
[... meta, base, title, link, etc.]
{{{injectScripts externalScripts}}}
</head>
<body>
[... etc]
</body>
</html>
I have a simple handlebars plugin called injectScripts which simply does that - it puts the scripts in where I call the it in the html. Not my favorite solution to a problem, but it works well enough.

How to load Vue Component with InertiaJS in Laravel 8 Modules

Working with Laravel Modules Package
In Laravel 8 we can load Component with Inertiajs
We initialize Inertia in app.blade.php with this #inertia
After that in app.js file, we can initialize it like this:
const app = document.getElementById('app');
new Vue({
render: (h) =>
h(InertiaApp, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
},
}),
}).$mount(app);
Then all components will load from the Pages folder.
Now, my question is:
How can I achieve it in a Module? I want to load Vuejs Components using Inertiajs. In module, this Resources/assets/js/app.js file is empty. If I put the above code in it and create a Pages folder but it's giving me error!
import { createApp, h } from 'vue'
import { App, plugin } from '#inertiajs/inertia-vue3'
const el = document.getElementById('app')
createApp({
render: () => h(App, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: name => require(`./Pages/${name}`).default,
})
}).use(plugin).mount(el)
In Core ( I made this ) module master.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Module Core</title>
<link rel="stylesheet" href="{{ mix('css/core.css') }}">
</head>
<body>
#inertia
<script src="{{ mix('js/core.js') }}"></script>
</body>
</html>
CoreController:
use Inertia\Inertia;
class CoreController extends Controller
{
public function index()
{
return Inertia::render('Admin/Index');
}
}
I have a Page in assets/js/Pages/Admin/Index.vue
I want to load this. But giving me error:
[Vue warn]: Error in created hook: "Error: Cannot find module './Admin/Index'"
Check if your Index vue page is inside this directory: resources/js/Page/Admin
if it is, copy all the contents inside your Index vue page and delete the Index vue file.
Recreate the file again but this time make sure you have type the file with the .vue extension
and try again
Laravel doesn't use assets folder inside js folder anymore.
So the right path should be
resources/js/app.js not Resources/assets/js/app.js
To make an Inertia response, use the Inertia render function. This method takes the component name, and allows you to pass props and view data.

Components do not render through <router-view /> in Laravel 5.8 with Vuejs

I'm trying to create an SPA using Vuejs and Laravel. I've installed vue-router to accomplish that. The component that has to be rendered inside the
<router-view />
tag is not rendering.
I've tried creating a new project and installing npm and vue-router again.
It still does not work.
Laravel Version : 5.8.18
vue-router : 3.0.6
welcome.blade.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
</head>
<body>
<div id = "app">
<router-view></router-view>
</div>
<script src = "js/app.js"></script>
</body>
</html>
app.js(./resources/js/app.js)
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
Vue.use(VueRouter)
const app = new Vue({
el: '#app',
router: new VueRouter(routes)
});
routes.js(./resources/js/routes.js)
import Home from './components/Home'
export default {
mode: 'history',
routes: [
{
path: '/',
component: Home
}
]
}
Home.vue(./resources.components/Home.vue)
<template>
<p>Home</p>
</template>
<script>
export default {}
</script>
web.php
<?php
Route::get('/{any?}', function () {
return view('welcome');
});
Your code works for me. Remember to use
npm run watch

Resources