Laravel/Vue Component Registration and Vue Instantiation - laravel

I am a new VueJs developer and there is something I can’t seem to get my head around: Using multiple single page components in the app.js file.
These are the questions I’m having trouble answering:
Am I supposed to register ALL of my top-level (parent) components in the Vue Instance in the app.js file?
// App.js
import Component1 from './component/C1.vue';
import Component2 from './component/C2.vue';
const app = new Vue({
el: '#app',
components: {All Top Level components here?}
});
If I need another Vue instance in one of my blade.php files. Where do I instantiate it? Is it best practice to only instantiate Vue once (in the el: #app in app.js) – this doesn’t seem right to me, but I’m new so what do I know.
How should I separate my Vue code from my Laravel Code in the main file? (create a separate section?)
<!DOCTYPE html>
<head>
<!-- App.js -->
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
#yield('content')
</div>
<div id="app2">
<!-- Where can I instantiate this?--> (Question 2)
</div>
</body>
#yield('vueScripts') <-- (Question 3)
</html>

Q1. You should register all your Vue Component that you will be using inside a blade file.
On the resource folder at the app.js
Vue.component('example-component', require('./components/ExampleComponent.vue').default); if you are just using a component on a single parent component you dont need to register the component globally you just register it in the parent component file like so
//On Parent Component file
import ChildComponent from './ChildComponent.vue' //import child component
export default
{ components: { ChildComponent } // child component registration
,
Q2. One Vue instance is enough
Q3. You dont need a vuescript if you are already using a vue component vue scripts should go in the vue component file

Related

Laravel View - Import Vue component

Getting to know laravel (using version 6) and i was wondering how i can make sure that my view is using my Vue component? In this case its the HelloWorld.vue component i want rendered in the albums.blade.php view file.
In my app.js i have made sure to register the component like this:
Vue.component('hello-world', require('vue\src\components\HelloWorld.vue').default);
And then im using it like this inside the albums.blade.php:
<hello-world></hello-world> and i have also made sure to include the script tag referring to app.js like this:
<script src="{{ asset('js/app.js') }}" defer></script>
With no success... any inputs?
See my folder structure here
you need to use laravel mix for vue js
Laravel Mix
change your <script src="{{ asset('js/app.js') }}" defer></script>
to <script src="{{ mix('js/app.js') }}" defer></script>
Place check Laravel Mix docs
My personal config using Laravel mix is:
app.js
require('./bootstrap');
import Vue from 'vue';
window.Vue = Vue;
/** Auto register components */
const files = require.context('./', true, /\.vue$/i)
files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default));
// Other things
// ....
const app = new Vue({
el: '#app'
});
albums.blade.php
Place this script tag at the bottom of the page.
<script src="{{ mix('js/app.js') }}"></script>
Then you could use the component like this:
<div>
<hello-world></hello-world>
</div>
Recommendation
Consider learning Laravel 9 instead of Laravel 6. This because 9 is the newest version and also the actual LTS release.

How to use Alpine.data() in #push within Laravel Blade components?

With Alpine.js 2 it was possible to develop Blade components with the following structure:
...
<div x-data="myApp()">
...
#once
#push('child-scripts')
<script>
function myApp() {
return {
...
}
}
</script>
#endpush
#endonce
This was great in that it allowed all the code for the component to be defined together.
In Alpine.js 3 this approach of defining functions is deprecated (https://alpinejs.dev/upgrade-guide#alpine-data-instead-of-global-functions). Instead, they should be defined like this:
...
<div x-data="myApp">
...
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('myApp', () => ({
...
}))
})
</script>
However, it states "you need to define Alpine.data() extensions BEFORE you call Alpine.start()". This suggests that with the new approach it is impossible (or at best unreliable) to use #push to add data functions for use in Blade components and, therefore, impossible to keep all the code for a Blade component together.
My code all works fine with Alipne 3, if I use the deprecated approach. If I try to include the new syntax data function via #push, I get "myApp is not defined" JS error from my Blade component.
I am using Alpine.js 3.9 installed as an NPM module. I have also tried using Alpine.js 3 via <script src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js" defer></script>. The result is the same in both cases.
Is there a way to keep all the code together in the Blade component?
UPDATE 25.02.2022
It seems I need to provide more details of what I am doing so here they are.
The relevant bits of my app.blade.php are:
<head>
...
<script src="{{ asset('js/app.js') }}" defer></script>
#livewireStyles
...
</head>
<body>
// HTML/Blade stuff here
#livewireScripts
#stack('child-scripts')
</body>
My app.js is like:
import Alpine from 'alpinejs'
require('./bootstrap');
window.Alpine = Alpine
Alpine.start();
I have also tried:
require('./bootstrap');
import Alpine from 'alpinejs'
window.Alpine = Alpine
Alpine.start();
My Blade component is like this:
...
<div x-data="myApp">
...
#once
#push('child-scripts')
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('myApp', () => ({
...
}))
})
</script>
#endpush
#endonce
When I access a page that uses the Blade component, I get "Uncaught ReferenceError: myApp is not defined".
If I revert my Blade component to the old syntax (as follows) it works with no errors:
...
<div x-data="myApp()">
...
#once
#push('child-scripts')
<script>
function myApp() {
return {
...
}
}
</script>
#endpush
#endonce
The only difference between the working and failing versions is the way in which myApp is defined. The deprecated method works but, with the new method, it doesn't.
Not sure why you would think that using #push is unreliable.
Since the #push directive (like any other blade directive) is evaluated by the blade engine on the server-side, by the time the page loads, anything you push is already there on the page.
One thing to note is that it is important to defer the execution of the javascript assets so that it is executed after the document has been parsed. You can do that by adding a defer attribute like so:
<!DOCTYPE html>
<html>
<head>
...
<script src="{{ mix('js/app.js') }}" defer></script> {{-- or CDN here --}}
</head>
<body>
...
#stack('scripts')
</body>
</html>
// resources/js/app.js
import Alpine from 'alpinejs'
window.Alpine = Alpine
Alpine.start()
I'm already using Alpine v3 this way without any issues.

Pass data from Blade template to Vue component [duplicate]

This question already has answers here:
Vue template or render function not defined yet I am using neither?
(28 answers)
Closed 1 year ago.
This is a new-comer question. I have a Blade template:
<div id="app">
<example-component
:componentMessage="appMessage"
></example-component>
</div>
<script>
var app = new Vue({
el: "#app",
data: function() {
return {
appMessage: {{$message}}
}
}
})
</script>
And a Vue component
resources/js/components/ExampleComponent.vue
<template>
<p>{{componentMessage}}</p>
</template>
<script>
export default {
props: ['componentMessage'],
}
</script>
So the data flow will look like this: $message -> appMessage -> componentMessage
The <example-component> is not working because I haven't imported it properly.
How am I supposed to use Example component inside Blade template? Providing that I'm using Vue CDN and I want to keep the app declaration right on the Blade template for fancy data transformation (with Blade syntaxes) before passing it to component with appMessage in the middle?
I have been searching for documents. Some requires app declaration in resources/js/app.js, but if I follow this way, I can not pass data to app with Blade mustache syntax.
Update:
resources/js/app.js
import ExampleComponent from "./components/ExampleComponent.vue";
I have tried adding
<script src="{{mix('js/app.js')}}"></script>
And added components declaration in the instance:
components: {
'example-component': ExampleComponent,
}
But it is still not working (Invalid Component definition: ExampleComponent). Probably I missed some steps.
Silly me. I do not need to declare components property in the app Vue instance. Just specify the component that you are going to use in resources/js/app.js and that component will be available globally in Blade template.
Watch for syntax differences between Laravel Mix differences tho. It could be the problem (Vue template or render function not defined yet I am using neither?)
import ExampleComponent from './components/ExampleComponent.vue';
Vue.component('example-component', ExampleComponent);

Vue Component not showing text when called from blade.php file

I have a basic vue 2.6.11 component that lives in a laravel 6 application. It lives at resources/js/components. I create a basic component and then in my app.js file I have vue imported and I define my vue component. Then in my app.blade.php I use the vue component but the text within my <template><div>Text here</div></template> does not appear on the screen. The <h1> text appears but not the vue component text. I looked at other posts but the vue versions other questions on here use are 2-3 years too old and don't apply. Any help is appreciated, thanks.
TableDraggable.vue
<template>
<div>
<h1>Test Text</h1>
</div>
</template>
<script>
export default {
mounted() {
console.log('this component has been mounted');
}
}
</script>
What I am using in my app.blade.php file
<h1>This is a test to see if VUE is working</h1>
<table-draggable></table-draggable>
app.js snippet
//Bring in VUE and vue draggable
window.Vue = require('vue');
import VueDraggable from 'vuedraggable';
Vue.component('table-draggable', require('./components/TableDraggable'));
In app.js try the following:
...
window.Vue = require('vue');
import VueDraggable from 'vuedraggable';
Vue.use(VueDraggable);
import TableDraggable from './components/TableDraggable';
Vue.component('table-draggable', TableDraggable);
const app = new Vue({
el: '#app',
data() {
return {};
},
});
...
Then in some parent element of where you are using your component, make sure it has an id of app. eg:
<div id="app">
<h1>This is a test to see if VUE is working</h1>
<table-draggable></table-draggable>
</div>
This is a basic example of getting vue working on your site, and not necessarily the best way to be structuring your assets depending on your needs. The biggest considerations about how to structure these are how bulky your assets will become if you include everything in app.js.

Laravel + VueJS - Component doesn't displayed

I'm trying to add VueJS in my Laravel app, but my first component is not displayed on my page.
As you can see on the source page, we can see the component "login".
And this is the associate code:
<template>
<p>Coucou je suis un élément Vue.JS</p>
</template>
<script>
export default {
name: "login"
}
</script>
<style scoped>
</style>
It's just a simple component that display a string.
Here you have the app.js
/**
* 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.component('login', require('./components/Auth/Login.vue'));
const app = new Vue({
el: '#app'
});
But my problem is that nothing appears on the html page :/ It's completely blank...
If someone could explain what's wrong, it will be so appreciate. Thank in advance.
Well, i found my mistake. I forget to add theses two lines :
<script src="{{ asset('js/manifest.js') }}"></script>
<script src="{{ asset('js/vendor.js') }}"></script>

Resources