Laravel Livewire: Bootstrap Select-Picker unable to render - laravel

I want to load categories and multiple sub categories according to all the categories that are selected, inside a multi-step form at step 4, but I am not able to select multiple categories using bootstrap selectpicker (multiple selects) library inside livewire component. The issue with using Bootstrap-Select with Livewire is that both of these libraries use JavaScript to update the page dynamically, which is probably causing conflicts. If I try accessing the same without livewire, it works perfectly fine, but with livewire I am unable to render it.
form.blade
#section('content')
<livewire:multi-step-form />
#endsection
#push('before-styles')
<link rel="stylesheet prefetch" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.1/css/bootstrap-select.css" />
#endpush
#push('before-scripts')
<script rel="javascript prefetch" src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.bundle.min.js"></script>
<script rel="javascript prefetch" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js" integrity="sha512-yDlE7vpGDP7o2eftkCiPZ+yuUyEcaBwoJoIhdXv71KZWugFqEphIS3PU60lEkFaz8RxaVsMpSvQxMBaKVwA5xg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
#endpush
multi-step-form.blade
<select wire:model="categories" name="categories[]" class="selectpicker form-control" multiple data-live-search="true" required id="categories" title="{{ trans('select category') }}">
#foreach($categories as $category)
<option value="{{$category->id}}" {{ (collect(old('categories'))->contains($category->id)) ? 'selected' : '' }}>
{{$category->category}}
</option>
#endforeach
</select>
Some Failed Attempts
Inside livewire component
Attempt 1
<script>
document.addEventListener('livewire:load', function () {
$('.selectpicker').selectpicker();
});
</script>
This works well but only once, i.e. at the start when livewire was loaded. This doesn't work, after the livewire component is updated by some input. So I added a code right below it:
Attempt 2
<script>
document.addEventListener('livewire:load', function () {
$('.selectpicker').selectpicker();
$('.selectpicker').selectpicker('render');
});
</script>
This didn't work too. Some few articles had render separated in updated script so I did that too.
Attempt 3
<script>
document.addEventListener('livewire:load', function () {
$('.selectpicker').selectpicker();
});
document.addEventListener('livewire:updated', function () {
$('.selectpicker').selectpicker('render');
});
</script>
Unfortunately this didn't work either.
I have tried adding the same scripts to my app.js too later, but am not sure where am I going wrong, and how can I make this working.
Attempt 4
Kept the scripts as is, and added wire:ignore in parent div
<div wire:ignore class="form-group">
<select wire:model="categories" name="categories[]" class="selectpicker form-control" multiple data-live-search="true" required id="categories" title="{{ trans('select category') }}">
#foreach($categories as $category)
<option value="{{$category->id}}" {{ (collect(old('categories'))->contains($category->id)) ? 'selected' : '' }}>
{{$category->category}}
</option>
#endforeach
</select>
</div>
As suggested in livewire/livewire/issues/45, but this didn't work as well.

Related

Laravel Livewire and select2 dropdown inside of bootstrap modal

I have a select2 blade component that I'm using inside of a bootstrap modal. Everything is working as expected, except when the modal form is submitted, the styling of the select 2 is lost, and a new empty option (must be the placeholder) is shown as an option, but only in the selects where I'm doing a yes/no option where the value is either 1 or 0 (See code below).
The styling is messed up on all, but the extra placeholder option only shows up on some.
Here's the normal way it should look, and does look when you first open the modal after a page refresh:
After submitting the modal it looks like this (notice the extra placeholder option):
Here's the code for my select2 component:
#props(['id', 'usePlaceholder' => null])
<div wire:ignore style="width:100%;" id="select2-{{ $id }}">
<select {{ $attributes }} id="{{ $id }}" name="{{ $id }}[]" class="form-select select2" data-width="100%" data-select2-id="{{ $id }}">
#if ($usePlaceholder)
<option></option>
#endif
{{ $slot }}
</select>
</div>
{{-- blade-formatter-disable --}}
#push('custom-scripts')
<script>
$(document).ready(function() {
let select2 = $('#' + #js($id));
select2.select2({
dropdownParent: $('#select2-' + #js($id)),
placeholder: 'Select an option',
minimumResultsForSearch: 15
});
***The below listeners I can comment out, they have no effect as far as I can tell, just including them for reference***
window.addEventListener('hideModal', event => {
select2.val(null).trigger('change');
});
window.addEventListener('set'+#js(ucfirst($id)), event => {
select2.val(event.detail).trigger('change');
});
});
</script>
#endpush
{{-- blade-formatter-enable --}}
And here's where I'm actually using the component:
<div class="col-md-6">
<x-input.group label="Sales Tax" for="salesTax" :error="$errors->first('salesTax')">
<x-input.select2 usePlaceholder="{{ empty($salesTax) ? 'true' : '' }}" wire:model.defer="salesTax" id="salesTax">
<option name="no" id="sales-tax-0" value="0">No</option>
<option name="yes" id="sales-tax-1" value="1">Yes</option>
</x-input.select2>
</x-input.group>
</div>
I've tried destroying and re-initializing the select2 when the modal is dismissed, but that hasn't done anything for me.

Making Vue Select component reusable (Laravel), returns [object Object]

I'm trying to create a reusable select component - initially, just to prove things were working I loaded the select directly in the vue file - which works with data.
I have the following in php controller:
$countries = Country::pluck('name', 'id');
return Inertia::render('Create', compact('countries'));
In the Create.vue file I tested this code which works:
<select class="form-control" v-model="selCountry">
<option v-for="(country, id) in countries" :key="id" :value="id">
{{ country }}
</option>
</select>
When I tried to make the select component reuseable, I would keep getting [object Object] being returned here is the code:
Select-Component.vue
<template>
<select
class="form-control"
#change="$emit('update:modelValue', $event.target.value)"
ref="select"
>
<option
v-for="(data, key) in options"
:key="key"
:value="key"
:selected="data === modelValue"
>
{{ data }}
</option>
</select>
</template>
<script>
export default {
props: ["modelValue", "options"],
emits: ["update:modelValue"],
methods: {
focus() {
this.$refs.select.focus();
},
},
};
</script>
Btw here is some example data that is being passed from php to vue:
101 => "Germany"
225 => "UK"
Thanks in advance.
and finally loading the component with:
<select id="countries" :options="countries" />
Update: sorry just to add I am trying to achieve this without using any npm packages.
Thanks

How to avoid livewire request for simple inputs calculations?

How would I avoid doing calculation for 3 inputs using Livewire and use JS instead, but still can bind the inputs and their new values with the component.
Example:
<input id="cash-price"
type="text"
wire:model="total_cache_price"
class="amount-credit">
<input id="deposit"
type="text"
wire:model="deposit"
class="amount-credit">
<input id="trade-in"
type="text"
wire:model="trade_in"
class="amount-credit">
I can easily do a simple calculation using JS, but the properties in Livewire component would still be empty or null after submitting the form. I am trying to avoid livewire requests for every input change.
Note: I understand the deferred updating in livewire, the problem is with the property values not changing.
I will show you an example in alpine js + livewire way.
Here I want to set value in two inputs from a selected option in select.
Please note that x-ref is used in alpine to directly access DOM elements without using getElementById.
<div x-data="{
from_date : #entangle('from_date').defer,
to_date : #entangle('to_date').defer,
dateChange(){
select = this.$refs.years;
this.from_date = select.options[select.selectedIndex].getAttribute('data-from');
this.to_date = select.options[select.selectedIndex].getAttribute('data-to');
},
resetFilters(){
this.net_balances = false;
}}">
<select x-ref="years" #change="dateChange()">
<option value="">Year</option>
#foreach ($years as $key => $year)
<option wire:key="{{ 'years'.$key }}" data-from="{{ $year->from_date }}" data-to="{{ $year->to_date }}" value="{{ $year->id }}">{{ $year->name }} </option>
#endforeach
</select>
<div>
<text type="date" x-model="from_date" />
</div>
<div >
<text type="date" x-model="to_date" />
</div>

Passing variable form blade template to vue component

I have a form in blade template. iF I submit the form I would like to pass id variable to vue component. How can I do it. Please help. Below my code.
<table>
#foreach($zam as $z)
<tr>
<td>{{$z->id}}</td>
<td>{{$z->name}}</td>
<td>{{$z->status}}</td>
<td>{{$z->getUser->name}}</td>
<td>
<form method="POST" action="{{route('mod_zam')}}">
#csrf
<select name="status">
<option value="przyjete"> przyjete</option>
<option value="realizowane"> realizowane</option>
<option value="wyslane"> wyslane</option>
</select>
<input type="hidden" name="id" value="{{$z->id}}" />
<input type="submit" name="modyf" />
</form>
</td>
</tr>
#endforeach
</table>
<ex id=???></ex>
I'm considering doing this in my blade:
const app = new Vue({
el: '#app',
data: {
},
});
But it's not working in blade. My second question - is it possible to create vue instance directly in blade, not via component?
To pass down data to your components you can use props.
You can do something like:
<component is="component" :zID="{{ $z->id }}"></component>
And then in your Example.vue file you have to define your prop. Then you can access it by this.zID.
<script>
export default {
props: ['zId'],
mounted () {
// Do something useful with the data in the template
console.dir(this.zId);
}
}
</script>
You can make inline templates directly in your blade views.
<component :props="{{ $props }}" inline-template>....</component>

Vue component select some options by default

UPDATE: the issues does not reproduce with "CDN" style. Here's the perfectly working JSBin: https://jsbin.com/ziqasifoli/edit?html,js,output
It looks that it is connected to something amongst webpack/gulf/elixir/vuefy.. I.e., all that Laravel 5.3 infrastructure. Not sure even where to start debugging.
...
I'm struggling with a simple (as I thought) component, that gets name, options and selected as input and displays multiple selectbox:
<!-- TagSelector.vue: -->
<template>
<select :name="name" v-model="selected" multiple>
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
</template>
<script>
export default {
props: ['name', 'options', 'selected'],
}
</script>
.
// app.js
Vue.component('tags-selector', require('./components/form_fields/TagsSelector.vue'));
const app = new Vue({
el: '#app'
});
.
<!-- some.form.blade.php -->
<tags-selector
class="form-control"
name="posted_data[tags]"
:selected="['opt1', 'opt2']"
:options='{!! json_encode($options) !!}'
></tags-selector>
It displays selector as expected, but no options pre-selected there.
How do I get it working?
To set some options as selected by default you will want to bind them to data, to do this simply make a copy of your selected prop and use v-model to bind it, which you can do with the created hook:
created: function() {
this.selectedCopy = this.selected;
},
data: function(){
return {
selectedCopy: []
}
}
Then simply bind them by using v-model:
<select :name="name" v-model="selectedCopy" multiple>
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
Here's the JSFiddle: https://jsfiddle.net/3cq1jjL8/

Resources