laravel livewire breaks styling (#push script) - laravel

I applied livewire on top of an already made laravel project.
It's simply applying wire:model to select like below.
<select wire:model="wired" id="#selector-01" >
<option>1</option>
<option>1</option>
</select>
and using the wire variable(wired) in if statement in for loop.
<select id="#selector-02">
#foreach($numbers in $number)
#if($some_id == $wired)
<option> some_id </option>
#endif
#endforeach
</select>
I use it only as a component.
all the data is controlled in the original laravel controller.
livewire component is called by #livewire('component-name')
and everything works, as I thought it would, except styling.
I think it has to do with using SlimSelect
At the end of livewire component-name.blade.php, I have SlimSelect styling just like the below.
#push('js')
<script>
new SlimSelect({
select: '#selector-01'
})
new SlimSelect({
select: '#selector-02'
})
</script>
#endpush
I tried moving the codes to
original.blade.php with #push and #stack
app.blade.php without #push
but still styling breaks.
I am thinking a solution might be adding few lines in update() in live wire controller to reinject the styling script.
am I going in the right direction?
Please help.

adding the below code fixed the problem.
<script>
document.addEventListener('livewire:update', function () {
// Your JS here.
new SlimSelect({
select: '#selector-01'
})
new SlimSelect({
select: '#selector-01'
})
})
</script>
you can also use livewire:onload instead of #stack and #push for loading styling onload
Reference to livewire doc

Related

I am stuck in Laravel Livewire in which I am using multiple select2 and it is a wizard form

I have a wizard in laravel livwire with 4 steps.
in the first step I have select box of select2 for multiple selection, everything works fine but when I go to step 2 and return back, the problems comes
Select2 distorts and loose it's styling
Selectbox does not contain it's selected values
I am new to livwire so do not enough about it.
Here is my blade component
I don't exactly know what is the purpose of wire:ignore but I used it from internet.
<div class="col-sm-12 col-md-6 col-xl-6 col-lg-6 mb-10">
<x-label for="vehicle_groups" class="required">Vehicle Groups</x-label>
<div wire:ignore>
<x-select-two class="form-select-solid" id="vehicle_groups" multiple>
<option value=""></option>
#foreach($vehicleGroups as $vehicleGroup)
<option value="{{ $vehicleGroup->id }}">{{ $vehicleGroup->group_name }}</option>
#endforeach
</x-select-two>
</div>
#error('vehicle_groups')
<x-error>{{ $message }}</x-error>
#enderror
</div>
<script>
document.addEventListener("livewire:load", () => {
$('#vehicle_groups').select2().on('change', function (e) {
var data = $('#vehicle_groups').select2("val");
#this.set('vehicle_groups', data);
});
});
</script>
here is my livewire component
<?php
namespace App\Http\Livewire;
use App\Models\Vgroup;
use Livewire\Component;
class Vehicle extends Component
{
public $currentPage = 1;
public $type, $vehicle_groups;
public function mount()
{
}
public function render()
{
$vehicleGroups = Vgroup::get(['id', 'group_name']);
return view('admin.livewire.vehicle', compact('vehicleGroups'));
}
public function gotToNextPage()
{
if ($this->currentPage === 1) {
$this->validate([
'type' => ['required'],
'vehicle_groups' => ['required']
]);
} elseif ($this->currentPage === 2) {
} else {
}
$this->currentPage++;
}
public function gotToPreviousPage()
{
$this->currentPage--;
}
public function submitForm()
{
$this->currentPage = 1;
}
}
wire:ignore you will use it, if you don't want to rerender a part of your code, for example imagine you have a js code, and when using it with livewire it can not work well, because livewire makes requests every time and the js code can't do it's job, now, you have to know that select2, creates it's own DOM and livewire keeps making requests(so you can see it's not a good combination), for that reason you need to add wire:ignore(to ignore select2).
So the problem that the styles doesn't render again, you could try adding the select 2 inside a DOMContentLoaded event:
document.addEventListener("DOMContentLoaded", () => {
$('#vehicle_groups').select2();
})
like this, the styles will be added when the page is loaded
on the other side, to get the data, you could try and add the ready event:
$(document).ready(function() {
$('#vehicle_groups').select2("val");
})
or
$(document).ready(function() {
$('#vehicle_groups').select2();
$('#vehicle_groups').val();
})
I see that you are using an event "livewire:load", in my case this event didn't work for me(in the inspector it throw me an error that the event didn't exist), you could try and use a hook instead of "livewire:load"
Livewire.hook('message.processed', (message, component) => {
})
this event will be called when all the requests of livewire are finished
https://laravel-livewire.com/docs/2.x/lifecycle-hooks
here is the link to the documentation if you want to check it
And last i want to comment something, i saw that you are using $vehicleGroup (i guess it's an array), you are getting the data on the view, all okei there, however you using the array on select 2, to overrite the data, is that what you want to do?(#this.set('vehicle_groups', data))
you could use the attribute wire:model(if you don't need to make requests every time in that select better use wire:model.defer, like this the only request that is going to make is when it's submited), to call a variable on livewire and store the value of the select
<x-select-two class="form-select-solid" wire:model="vehicle_variable" id="vehicle_groups" multiple>
<option value=""></option>
#foreach($vehicleGroups as $vehicleGroup)
<option value="{{ $vehicleGroup->id }}">
{{$vehicleGroup->group_name }}
</option>
#endforeach
</x-select-two>
wire:model="vehicle_variable"
now on livewire:
public $vehicle_variable
and on select2:
#this.set('vehicle_variable', data)
now you have the value on a variable and not an array(if it's an array)
to check if the data exists, you could use the debugger of laravel "ddd()" inside the function mount (the mount function, will be called when the page is being loaded):
public function mount()
{
ddd($vehicle_variable);
}
if it's not null, then select2 should work however, if the value is null you need to check your livewire
if you have any question, just ask and i will try to answer!

Livewire ignore does not receive correct data

I'm using selectize.js for dropdown styling with livewire. I'm using livewire for a data table with sortable columns and pagination. The issue is, every time I make pagination or a sort by column, the javascript goes missing thus, there's no styling for the dropdown. I've solved the styling issue using wire:ignore. Now the new problem that I have is that the data passed to the dropdown is not accurate.
#foreach($applications as $application)
<p>{{$application->status}}</p>
<div wire:ignore>
<select class="selectize" name="status" data-width="200px">
#foreach(['Pending',
'Hired',
'Under consideration'] as $status)
<option
#if($application->status === $status) selected #endif>{{ $status }}</option>
#endforeach
</select>
</div>
#endforeach
Inside the <p>{{$application->status}}</p> tag, I get the status 'Pending' but on the dropdown, it shows 'Hired'. The correct status is 'Pending'.
(from comment) #stalwart1014 for example, when I use "select2" I do this
in content section
<select id="select2" wire:model="some">
//.....
</select>
in script section
$(document).ready(function() {
window.initSelectDrop=()=>{
$('#select2').select2({
placeholder: '{{ __('Select') }}',
allowClear: true});
}
initSelectDrop();
window.livewire.on('select2',()=>{
initSelectDrop();
});
});
and in component
public function hydrate()
{
$this->emit('select2');
}
This not always function properly with JS element...but hope this can help you. Greetings
it's maybe a bit late, but worth to mention for the future, I will do not point directly to your issue but explain the workaround of wire:ignore, the wire:ignore directive ignores the updates or changes for the further requests and updates, this attributes is defined for playing with JS third party library, the wire:ignore directive prevent all it's nested elements from updating if you wish to except Childs from updating you can use wire:ignore.self directive

Laravel + Bootstrap-Vue Table not accepting data

I'm currently playing with Laravel Spark and I'm slowly learning the Vue.js system.
I have a bunch of data I want to display in a table that is pulled from AWS DynamoDB. I am successfully parsing this data in sorts of ways and can display the data in a standard static Bootstrap table. I'm now trying to use the Vue.js version and I cannot for the life of me get this data to display at all. If I insert dummy data into the Vue Component, the dummy data shows so it must be the way I'm passing the data in.
My code as follows:
TableController.php
public function show()
{
$data = $this->fetchAWSData($condition); // This is my separate AWS method
return view('table')->with('items', $data);
}
table.blade.php
#extends('spark::layouts.app')
#section('content')
<home :user="user" inline-template>
<div class="container-fluid" style="text-align: left">
<h1>Data</h1>
<MyTable items={{ $items }}></MyTable>
</div>
</home>
#endsection
MyTable.vue
<template>
<b-table striped hover :items=items></b-table>
</template>
<script>
export default {
data() {
return {
items: this.items
}
}
}
</script>
What am I doing wrong here? I've tried formatting my data all sorts of ways; JSON, manually, Arrays... nothing works. So it must be the way I'm passing it in.
Any insight would be AMAZING :)
You have to use props to be able to pass attributes to Vue's components.
MyTable.vue
...
<script>
export default {
props: ['items'],
}
</script>
Then you can pass data to component:
<MyTable :items="{{ $items }}">

Pass data from blade to vue component

I'm trying to learn vue and with that I want to integrate it with laravel too..
I simply want to send the user id from blade to vue component so I can perform a put request there.
Let's say I have this in blade:
<example></example>
How can I send Auth::user()->id into this component and use it.
I kept searching for this but couldn't find an answer that will make this clear.
Thanks!
To pass down data to your components you can use props. Find more info about props over here. This is also a good source for defining those props.
You can do something like:
<example :userId="{{ Auth::user()->id }}"></example>
OR
<example v-bind:userId="{{ Auth::user()->id }}"></example>
And then in your Example.vue file you have to define your prop. Then you can access it by this.userId.
Like :
<script>
export default {
props: ['userId'],
mounted () {
// Do something useful with the data in the template
console.dir(this.userId)
}
}
</script>
If you are serving files through Laravel
Then here is the trick that you can apply.
In Your app.blade.php
#if(auth()->check())
<script>
window.User = {!! auth()->user() !!}
</script>
#endif
Now you can access User Object which available globally
Hope this helps.
Calling component,
<example :user-id="{{ Auth::user()->id }}"></example>
In component,
<script>
export default {
props: ['userId'],
mounted () {
console.log(userId)
}
}
</script>
Note - When adding value to prop userId you need to use user-id instead of using camel case.
https://laravel.com/docs/8.x/blade#blade-and-javascript-frameworks
Rendering JSON
Sometimes you may pass an array to your view with the intention of rendering it as JSON in order to initialize a JavaScript variable. For example:
<script>
var app = <?php echo json_encode($array); ?>;
</script>
However, instead of manually calling json_encode, you may use the #json Blade directive. The #json directive accepts the same arguments as PHP's json_encode function. By default, the #json directive calls the json_encode function with the JSON_HEX_TAG, JSON_HEX_APOS, JSON_HEX_AMP, and JSON_HEX_QUOT flags:
<script>
var app = #json($array);
var app = #json($array, JSON_PRETTY_PRINT);
</script>
Just to add for those who still get error.
For me this <askquestionmodal :product="{{ $item->title }}"></askquestionmodal> still gives error in console and instead showing html page I saw white screen.
[Vue warn]: Error compiling template:
invalid expression: Unexpected identifier in
Coupling to connect 2 rods М14 CF-10
Raw expression: :product="Coupling to connect 2 rods М14 CF-10"
Though in error I can see that $item->title is replaced with its value.
So then I tried to do like that <askquestionmodal :product="'{{ $item->title }}'"></askquestionmodal>
And I have fully working code.
/components/askquestionmodal.vue
<template>
<div class="modal-body">
<p>{{ product }}</p>
</div>
</template>
<script>
export default {
name: "AskQuestionModal",
props: ['product'],
mounted() {
console.log('AskQuestionModal component mounted.')
}
}
</script>

Vue.js Calling function on a rendered component

I want to create an interactive scrumboard using Laravel and Vue.js containing multiple columns and within those columns multiple tickets.
These tickets are vue components with some nice edit / delete / (un)assign developer functionality and is used on other pages as well.
I have multiple columns defined like this:
<div id="scrumboard">
<div class="scrumboard__column">
<div class="scrumboard__title">Open</div>
<div class="scrumboard__tickets_wrapper" data-status="open">
#if( $sprint->hasTicketsOfStatus("open") )
#foreach( $sprint->getTicketsByStatus("open") as $ticket )
<ticket :data="{{ $ticket->getJsonData(true) }}"></ticket>
#endforeach
#endif
</div>
</div>
<div class="scrumboard__column">
<div class="scrumboard__title">In progress</div>
<div class="scrumboard__tickets_wrapper" data-status="progress">
#if( $sprint->hasTicketsOfStatus("progress") )
#foreach( $sprint->getTicketsByStatus("progress") as $ticket )
<ticket :data="{{ $ticket->getJsonData(true) }}"></ticket>
#endforeach
#endif
</div>
</div>
<div class="scrumboard__column">
<div class="scrumboard__title">Finished</div>
<div class="scrumboard__tickets_wrapper" data-status="closed">
#if( $sprint->hasTicketsOfStatus("closed") )
#foreach( $sprint->getTicketsByStatus("closed") as $ticket )
<ticket :data="{{ $ticket->getJsonData(true) }}"></ticket>
#endforeach
#endif
</div>
</div>
</div>
And as you can see it renders a ticket component for each ticket it finds for each column.
No i have turned the scrumboard__tickets_wrapper div's into jquery ui sortable lists which allows you to swap the tickets between columns.
<script>
$(document).ready(function(){
$(".scrumboard__tickets_wrapper").sortable({
connectWith: '.scrumboard__tickets_wrapper',
receive: function(event, ui){
console.log("Switched columns");
console.log(event);
console.log(ui);
var target = $(event.target);
target.css("background-color", "#ff0000");
}
});
</script>
Everything is working so far, now my question is: how do I dynamically call the "updateStatus()" function on a ticket component once the ticket is dropped into another list?
As you can see I can get the specific element being dropped and the sortable list it's been dropped into. So I know what the new status is by grabbing the data-status property of the wrapper + I know which element was dropped.
But how do I grab the instance of the ticket component in question and call the updateStatus function to save the new status?
Thanks in advance!
Screenshot of the scrumboard
Thanks David for pointing me in the right direction. The solution to my problem was proper component nesting.
The solution was to create 3 components with proper child-component inheritence. And declaring the child-components within the template of it's parent.
This way I end up only declaring "" and the magic happens :D.
So I have made 3 components:
- scrumboard > takes scrumboardColumn as component
- scrumboardColumn > takes ticket as component
- ticket
The root vue instance also loads the ticket component since the ticket component is also used on the backlog page.
I haven't completely finished the final product but I got the sortable working by calling it from within the ready function of the scrumboardColumn component like David suggested.
Hope this helps someone in the future!

Resources