Livewire uncaught (in promise) DOMException with select - laravel

I have a dropdown in my component that seems to be causing an error in Livewire.
Uncaught (in promise) DOMException: Failed to execute 'setAttribute'
on 'Element': '?' is not a valid attribute name.
I added the wire:key code based on what I read in the docs under the troubleshooting section, but it didn't seem to help. It updates the value on save just like it should, but when it refreshes the dom with the new data, it's not working.
Here's the offending element:
<div class="mb-3 col-md-4 ">
<label for="state" class="form-label">State</label>
<select wire:model.defer="location.state" class="form-select"
id="state" name="state">
#foreach ($states as $state)
<option
name="{{ $state->name }}"
id="{{ $state->code }}"
wire:key="{{ $state->id }}"
value="{{ $state->code }}"
#if (isset($location->state)
&& $location->state === $state->code) ? selected #endif
>{{ $state->name }}
</option>
#endforeach
</select>
</div>

You have a typo here,
#if (isset($location->state) && $location->state === $state->code)
? selected
#endif
Where it tries to apply ? as an attribute on the element, but ? is not a valid attribute. Just remove the ?.
That said, you cannot use the selected property on <select> elements in Livewire to set the selected value. You need to set the value of your wire:model in your component. This means that you have to remove the #if(..) selected #endif from your Blade entirely, and instead set the value in your component,
public function mount() {
$this->location['state'] = $state->code;
}

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.

Laravel Livewire radio input not returning its value on the component view

hello I'm new to livewire, I created a component for the radio buttons and I'm trying to show the selected radio value on the component view but its always giving null no matter what is the checked radio its always null
The Component View
#foreach ($brands as $brand)
<div class="template-checkbo">
<input wire:model="brandsVal" name="brandsVal" type="radio" value="{{ $brand->id }}">
<label class="main-label">
{{ $brand->name }}
</label>
</div>
#endforeach
#dump($brandsVal)
The Component Class
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class BrandSidebar extends Component
{
public $brands;
public $brandsVal;
public function render()
{
return view('livewire.brand-sidebar');
}
}
I found the solution after opening the console and seeing a Warning not an error
Livewire: Multiple root elements detected. This is not supported.
Apparently The view component must have one root element other wise the whole functionality wont work according to this doc:
https://laravel-livewire.com/docs/2.x/troubleshooting#root-element-issues
I think the issue is with this here
wire:model="brandsVal"
The issue with the current implementation is that all of the radio buttons have the same name attribute "brandsVal" and the same wire:model value "brandsVal", so when one of the buttons is selected, all of the buttons will have the same value.
in your component class,
try this instead
//from public $brandsVal;
public $brandsVal = []; //sets the brandsVal property to an array.
Now generate the input elements in your blade file like this
#foreach ($brands as $brand)
<div class="template-checkbo">
<input wire:model="brandsVal.{{ $brand->id }}" type="radio" value="{{ $brand->id }}">
<label class="main-label">
{{ $brand->id }}
</label>
</div>
#endforeach
Secondly, are you passing anything here? public $brands;
Lastly, make sure to wrap your livewire blade file into just one html element.

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>

Property [kodeSparepart] does not exist on this collection instance

I want to get all of the data from sparepart database using all() function, then use foreach in view to access the data, but I keep get that error. It works fine when I use the same method for other view blade.
Controller
public function LaporanSisaStok(Request $request) {
if($request->kode == "")
{
$spareparts = Sparepart::all();
return view('laporan/sisaStok')->with(['spareparts' => $spareparts]);
}
else {
$query = DB::table("historisparepart")->select(DB::raw('EXTRACT(MONTH FROM tanggal) AS Bulan, SUM(jumlah) as Sisa'))
->where('kodeSparepart', $request->kode)
->groupBy(DB::raw('EXTRACT(MONTH FROM tanggal)'))
->get();
return view('printPreview/sisaStok', ['data'=>$query]);
}
}
View
<form method="POST" action="{{ route('laporan.sisaStok') }}" enctype="multipart/form-data">
#csrf
<div class="form-group-row">
<label for="sparepart" class="col-sm-2 col-form-label">Sparepart</label>
<select class="custom-select" id="kode" name="kode">
<option value="">-Pilih Sparepart-</option>
foreach($spareparts as $sparepart)
{
<option value="{{$sparepart->kodeSparepart}}"> {{$sparepart->namaSparepart}} </option>
}
</select>
</div>
<br>
<button type="submit" class="btn btn-info"><i class="oi oi-task"></i> Cari </button>
You aren't looping through anything. You need to give the foreach() method a variable from your spareparts collection. I think it might help you avoid confusion, to name the collection variables plural:
In your controller:
$spareparts = Sparepart::all();
return view('laporan/sisaStok', compact('spareparts'));
Then, most importantly, you need to tell foreach what it should produce. In your view, change:
foreach($sparepart)
to
#foreach($spareparts as $sparepart)
Don't forget you are in blade, so use the # before the foreach. Then, assuming you actually have a property on the spareparts model called kodeSparepart, this should work fine.
This is not a looping syntax in the laravel view
foreach($spareparts as $sparepart)
{
<option value="{{$sparepart->kodeSparepart}}"> {{$sparepart->namaSparepart}} </option>
}
It should be like this
#foreach($spareparts as $sparepart)
<option value="{{$sparepart->kodeSparepart}}"> {{$sparepart->namaSparepart}} </option>
#endforeach
And another problem is you are passing the data in wrong way. It should be like this
return view('printPreview/sisaStok', ['spareparts'=>$query]);
You shoud use the plural of your dtb name to loop over the values so your 'sparepart' variable should change to 'spareparts'
$sparepart = Sparepart::all();
return view('laporan/sisaStok')->with(['spareparts' => $sparepart]);
In your view change your loop to the new variable and loop using your current variable, so your view shoul look like this:
View
<div class="form-group-row">
<label for="sparepart" class="col-sm-2 col-form-label">Sparepart</label>
<select class="custom-select" id="kode" name="kode">
<option value="">-Pilih Sparepart-</option>
#foreach($spareparts as $sparepart)
{
<option value="{{$sparepart->kodeSparepart}}"> {{$sparepart->namaSparepart}} </option>
}
#endforeach
</select>
</div>

Laravel 5 - Pre-populate a HTML select using database value and old

I am trying to use 'old' in a Laravel 5 app to pre-populate a select form on an edit route like this...
<select id="category" name="category" required>
<option value="">Select</option>
<option value="animal" #if (old('category') == "animal") {{ 'selected' }} #endif>Animal</option>
<option value="vegetable" #if (old('category') == "vegetable") {{ 'selected' }} #endif>Vegetable</option>
<option value="mineral" #if (old('category') == "mineral") {{ 'selected' }} #endif>Mineral</option>
</select>
This works well and keeps the selected option if a validation fails, but I am trying to make it work so that it pre-populates when the page first loads.
How do I determine if this is the first load of the edit page, or if it has reloaded after a validation failure? Or is there a better way to do this?
Lets imagine you have sent the category value as $category.
So in every <option> tag,
<option value="animal"
{{ old('category') == 'animal' ? ' selected' :
$category == 'anumal' ? ' selected' : '' }}>
Animal
</option>
This is what is going on there:
if there is an old value and its matching, select this,
else if the original value is matching select this,
else do nothing.
Use the second parameter of the old function with a default/initial value:
<option value="animal" #if (old('category', 'animal') == "animal") {{ 'selected' }} #endif>Animal</option>
The second parameter will be returned as the function result if an old value cannot be found in the flashed input.

Resources