laravel livewire $wire is not defined - laravel

I think I have all my imports right, but I'm getting a $wire is not defined error.
This is at the bottom of my master layout blade file:
#livewireScripts
<script src="{{ asset('js/app.js') }}"></script>
#stack('plugin-scripts')
#stack('custom-scripts')
In my livewire component I have this:
#push('custom-scripts')
<script>
$(document).ready(function() {
console.log($wire.find($('#my-div').attr('id')));
});
</script>
#endpush
I was reading that $wire is an Alpine directive, and here's where I'm including Alpine. I can also type Alpine in the console and it is registered, so I believe that's working.
in my bootstrap.js:
import Alpine from 'alpinejs'
window.Alpine = Alpine
window.Alpine.start()
Also if I type Livewire in the console, it returns the Livewire object, so that seems to be getting loaded correctly as well. However, I added this bit to my code and it did nothing:
document.addEventListener('livewire:load', function() {
console.log('livewire loaded'); // Your JS here.
});

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);

#click showing ReferenceError: function is not defined with AlpineJS + Livewire + Laravel

Am totally lost here. I've made multiple applications with Livewire + AlpineJS + Laravel before, but for some reason I cannot get the #click functionality working for functions in this app.
I reduced the code to almost nothing and cannot see the issue. Have looked at other apps where I have #click working.
<div x-data="showAppSettings()">
<div #click="whatever(123)">Click here</div>
<script type="text/javascript">
window.showAppSettings = () => {
return {
showNav: false,
whatever(id)
{
}
};
}
</script>
Upon clicking the click me and triggering the function, I get the error ReferenceError: whatever is not defined.
Works just fine if I do something like #click="alert('hey')"
What am I missing?
The issue was related to including <script src="{{ asset('js/app.js') }}" defer></script>
That file was not even in use. But either way, the error was being triggered by app.js which I realized made no sense since the code was inline.
Upon removing that line, everything works as intended.
It's needed the defer attribute to load fine the alpine script:
<script defer src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js"></script>

react not receiving prop from html

I have a react component in a file named ts.js:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
export default class Ts extends React.Component {
constructor(props) {
super(props);
var expected = {
lowercase:'Onlylowercase',
snakeCase:'justSnakeCase',
ProperCase: 'AndProperCase'
};
console.log("expected:",expected);
console.log("props:",props);
console.log("this.props",this.props);
console.log("props.lowercase",props.lowercase);
this.state={'lowercase':this.props.lowercase};
};
render() {
return NULL;
}
}
if (document.getElementById('ts')) {
ReactDOM.render(<Ts />, document.getElementById('ts'));
}
I also have a html page from where this is called:
<html>
<head>
<title>My TS</title>
</head>
<body>
<Ts lowercase="onlylowercase" id="ts" snakeCase="justSnakeCase" ProperCase="AndProperCase">
</Ts>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
My issue is I can't get the values lowercase="onlylowercase" id="ts" snakeCase="justSnakeCase" ProperCase="AndProperCase" recognised as props in the constructor. I need to pass in some stuff from the html to populate the initial state of the component.
When I open the HTML with Chrome console open I get:
expected: {lowercase: "Onlylowercase", snakeCase: "justSnakeCase", ProperCase: "AndProperCase"}
props: {}
__proto__: Object
or it is this.props?: {}
__proto__: Object
props.lowercase undefined
this.props.lowercase undefined
undefined
undefined
I am expecting props to be a javascript object with properties of lowercase, snakeCase and ProperCase, like the var expected.
I don't think I need to use componentWillReceiveProps - as I am trying to follow the pattern describe in the documentation here:
https://reactjs.org/docs/react-component.html#constructor
and pass in props as html attributes as described here:
https://reactjs.org/docs/components-and-props.html
I have excluded from this post the detail of the node modules and javascript includes - as the Ts component's constructor is being called which demonstrates the Ts class is "there" and my npm config is OK - it is including react and other required modules. The {{ asset() }} function is a Laravel function. The html is part of a blade template in a Laravel app.
Can anyone see what I am doing wrongly?
Your syntax is wrong. React doesn't creat a new html tag like "". You only can use tag in react component. So the right syntax is in html replace
<Ts lowercase="onlylowercase" id="ts" snakeCase="justSnakeCase" ProperCase="AndProperCase">
</Ts>
To <div id="ts"></div>
and go add to before
<script>
var lowercase="whatever";
var snakeCase="snakeCase";
...
</script>
And change to
if (document.getElementById('ts')) {
ReactDOM.render(<Ts lowercase={lowercase} snakeCase={snakeCase} />, document.getElementById('ts'));
}
ReactDOM will find a dom with id is "ts" and replace it by your ts component.

Resources