Laravel Select2 old input after validation - ajax

I'm using Select2 in my webapplication. I load my Select2 boxes with Ajax. When validation fails, all the inputs are filled as before except the Select2 box. How can I restore the old value after the form validation fails? My bet was using Request::old('x'), but this inserts the value (in my case an user ID) instead of the selected text. So for example the text John would become 27 in the selectbox. How can I get the text back?
<select id="customer" name="customer" class="searchselect searchselectstyle">
</select>
The js:
token = '{{csrf_token()}}';
$(".searchselect").select2({
ajax: {
dataType: "json",
type: "POST",
data: function (params) {
return {
term: params.term,
'_token': token,
'data' : function(){
var result = [];
var i = 1;
$('.searchselect').each(function(){
result[i] = $(this).val();
i++;
});
return result;
}
};
},
url: function() {
var type = $(this).attr('id');
return '/get' + type;
},
cache: false,
processResults: function (data) {
return {
results: data
};
}
}
});
Edit
The only (dirty) solution I found so far is the following:
<select id="customer" name="customer" class="searchselect searchselectstyle">
#if(Request::old('customer') != NULL)
<option value="{{Request::old('customer')}}">{{$customers->where('id', intval(Request::old('customer')))->first()->name}}</option>
#endif
</select>
$customers is a list of all customers, so this means that for each Select2 box I need to query a big list of items in order to make it work. This will be pretty inefficient if we're talking about thousands of rows per Select2 box.
I guess there must be a better solution. Who can help me?

Normally to programmatically set the value of a select2, you would expect to use the .val() method followed by a .trigger('change') call as per their documentation (and other queries like this on SO). However, select2 themselves have something in their documentation about preselecting options for remotely sourced data.
Essentially their suggestion boils down to (after initalizing your AJAX-driven <select>):
make another AJAX call to a new API endpoint using the pre-selected ID
dynamically create a new option and append to the underlying <select> from a promise function (.then()) after the AJAX call is finished
could also use some of the regular jQuery callback chaining functions for this
trigger a change event
trigger a select2:select event (and pass along the whole data object)
Assuming you're already flashing the old data to the session, Laravel provides handy access to the previously requested input in a variety of ways, notably these three:
static access via the Request class e.g. Request::old('customer') as in the OP
the global old() helper e.g. old('customer'), which returns null if no old input for the given field exists, and can have a default as a second parameter
using the old() method on the Request instance from the controller e.g. $request->old('customer')
The global helper method is more commonly suggested for use inside Blade templates as in some of the other answers here, and is useful when you don't need to manipulate the value and can just plug it straight back in, which you would with things like text inputs.
The last method probably provides you with the answer you're looking for - instead of querying the entire collection from inside of the view, you're able to either manipulate the collection from the controller (similar to the OP, but should be nicer since it's not parsing it in the view) or make another query from the controller based on the old ID and fetch the data you want without having to trawl the collection (less overhead):
$old_customer = Customer::find($request->old('customer'));
Either way, you'd have the specific data available at your fingertips (as a view variable) before the blade template processes anything.
However you choose to inject the data, it would still follow the pattern suggested by select2:
get the pre-selected data
create an option for it
trigger the appropriate events
The only difference being you don't need to fetch the data from another API endpoint (unless you want/need to for other programmatic reasons).

I end up using similar flow like your. But my blade template is using htmlcollection package.
Controller:-
Let's say you are in create() method. When validation failed, it will redirect back to the create page. From this page, you can repopulate the list.
$customer_list = [];
if(old('customer') != NULL){
$customer_list = [old('customer') => $customers->where('id', old('customer'))->first()->name];
}
Blade View:
{{ Form::select('customer', $customer_list, null, ['class' => 'searchselect searchselectstyle', 'id' => 'customer']) }}

I did it with an input hidden for the text and it works well:
This form is showed in a Popup and ajax (using Jquery-UJS)
Form:
<form action="{{ route('store_item', $order) }}" method="POST" data-remote="true">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('item_id') ? ' has-error' : '' }}">
<label class="control-label col-sm-2" for="item_id">Item: </label>
<div class="col-sm-10">
<select name="item_id" class="form-control" id="item_id">
#if(old('item_id') != null)
<option value="{{ old('item_id') }}" selected="selected">
{{ old('item_title') }}
</option>
#endif
</select>
</div>
{!! $errors->first('item_id', '<p class="text-center text-danger"<strong>:message</strong></p>') !!}
</div>
<input type="hidden" id="item_title" name ="item_title" value="{{ old('item_title') }}" />
<div class="form-group{{ $errors->has('quantity') ? ' has-error' : '' }}">
<label class="control-label col-sm-2" for="quantity">Cantidad: </label>
<div class="col-sm-10">
<input name="quantity" type="number" class="form-control" id="quantity" value="{{ old('quantity') }}"/>
</div>
{!! $errors->first('quantity', '<p class="text-center text-danger"><strong>:message</strong></p>') !!}
</div>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-primary" data-disable-with="Guardando...">Guardar</button>
</form>
JAVASCRIPT:
<script type="text/javascript">
$(document).ready(function(){
$('#item_id').select2({
placeholder: 'Elige un item',
ajax: {
url: '{{ route('select_item_data') }}',
dataType: 'json',
delay: 250,
processResults: function (data) {
return {
results: $.map(data, function (item) {
return {
text: item.title,
id: item.id
}
})
};
},
cache: true
}
});
$('#item_id').on('change', function(e){
var title = $(this).select2('data')[0].text;
$('#item_title').val(title);
});
});
</script>
VALIDATION IN STORE METHOD (CONTROLLER):
$validator = Validator::make($request->all(), [
'item_id' => 'required',
'quantity' => 'required'
]);
if ($validator->fails()) {
return redirect()
->route('create_item', $order)
->withInput($request->all())
->withErrors($validator);
}
It's very important to send 'withInput' and 'withErrors' in the redirection, because we are working with a popup and ajax that is created again and doesn't keep the old values.

Maybe you can try (once the ajax call has ended) :
var oldCustomer = $('#customer > option[value={{ Request::old('customer') }}]');
if (oldCustomer.length > 0) {
oldCustomer.attr('selected', 'selected');
}

Same problem; I'm using a similar solution: If the old $id is set, I get the name and I use it as a variable for the view; Note that I also forward the id because I also used this method to pre-fill the form (coming from another place), but in this case, the name only should have been used, and for the id {{ old('author_id') }} can be used in the view:
In the controller:
elseif (($request->old('author_id') !== null) && ($request->old('author_id') != '')) {
$my_author_id = $request->old('author_id');
$my_name = Author::find($my_author_id)->name;
return view('admin/url_author.create', compact('my_name', 'my_author_id'));
}
And in the view (more precisely, in a partial used for creation & edition):
#if (isset($record)) // for use in edit case with laravelcollective)
<select class="form-control js-data-author-ajax" id="author_id" name="author_id">
<option value="{{ $record->author_id }}">{{ $record->author->name }}</option>
</select>
#else
#if (isset($my_name)) // old input after validation + pre-filling cases
<select class="form-control js-data-author-ajax" id="author_id" name="author_id">
<option value="{{ $my_author_id }}">{{ $my_name }}</option>
</select>
#else // for create cases
<select class="form-control js-data-auteur-ajax" id="auteur_id" name="auteur_id">
<option></option>
</select>
#endif
#endif

Your code is bit confusing. I don't understand why you are using a POST request to get data using ajax to fill a select2 box.
Assuming the data returned using ajax call is in the below format.
[
{
"id": "Some id",
"text": "Some text"
},
{
"id": "ID 2",
"text": "Text 2"
},
]
Now what you can do is pass in an extra parameter to your ajax call as below
url: function() {
var type = $(this).attr('id');
#if(old('customer'))
return '/get' + type + '?customer='+ {{ old('customer') }};
#else
return '/get' + type;
#endif
}
Now in your controller while returning data you can throw an extra attribute selected:true for an ID matching that particular ID.
if( Request::has('customer') && Request::input('customer') == $id )
{
[
"id" => $id,
"text" => $text,
"selected" => "true"
]
}
else
{
[
"id" => $id,
"text" => $text,
]
}

If I understood you right I can recommend you to have for each your select2 box hidden input <input type="hidden" name="customer_name" value="{{old('customer_name', '')}}"> where after change event for select2 you can insert selected name (etc. John). So if validation is fails you have:
<select id="customer" name="customer" class="searchselect searchselectstyle">
#if(!is_null(old('customer')))
<option value="{{old('customer')}}">{{old('customer_name')}}
</option>
#endif
</select>

I think your own solution is pretty much correct. You say the list of $customers will get pretty big.
$customers->where('id', intval(Request::old('customer')))->first()
Do you need to have the list stored in a variable $customers? You could just search the id you want
App\Customer::where('id', intval(Request::old('customer')))->first()
Searching by id should not be inefficient. Otherwise you could send the name with the form and store it in the old request. Shown below with some (dirty) javascript.
$("#form").submit( function() {
var sel = document.getElementById("customer");
var text= sel.options[sel.selectedIndex].text;
$('<input />').attr('type', 'hidden')
.attr('name', "selected_customer_name")
.attr('value', text)
.appendTo('#form');
return true;
});
Then like yrv 16s answer:
<option value="{{old('customer')}}">{{old('selected_customer_name')}}

You could do something like this:
First in controller pass tags to view using pluck helper like below:
public function create()
{
$tags= Customer::pluck('name','name');
return view('view',compact('tags'));
}
Then in your form try this:
{!! Form::select('tag_list[]',$tags,old('tag_list'),'multiple','id'=>'tag_list']) !!}
Don't forget to call the select2 function.
$('#tag_list').select2();
And finally in controller:
public function store(ArticleRequest $request)
{
$model = new Model;
$tags=$request->input('tag_list');
$model->tag($tags);
}
Notice tag function is not a helper in Laravel, You implement it! The function takes names and attaches them to the instance of some thing.
Good Luck.

Related

Not geeting response of ajax call from Laravel 9 function

I am trying to get the value of product price from pricing table. For this reason I want user to select the product and in the next field of price the value of product-price will be automatically taken from pricing database table based on product_id passed via ajax from controller.
enter image description here
My route code
public function getPrices(Request $request){$price = \DB::table('pricings')//->select('product_price')->where('product_id',$request->product_id)->get();//dd($request->product_id);if (!empty($price)){return response()->json($price) ;}
}
Above is function in controller to fetch the value of product-price based on 'product_id'.
$(document).ready(function() {$('#productID').on('change', function () {//alert('something changed');var productId = this.value;$('#productPrice').html('');//alert(productId);$.ajax({type: 'get',url: '{{ route("getPrices") }}',data : {product_id:productId},
success: function (response) {
//$('#productPrice').val(res);
alert(response);
},
dataType : "text",
error:function (response){
alert('Error'+response);
}
});
//alert(productId);
});
} );
my ajax code to set the value of price after selecting product.
<th scope="row">1</th><td class="col-sm-3"><div class="form-group row"><div class="col-sm-12"><select class="form-select" name="product_id" id="productID" aria-label="Default select example"><option value="" disabled>Select Product</option>#foreach($product as $products)<option data-tokens="{{ $products->product_name }}" value="{{ $products->id }}" >{{ $products->product_name}} | {{ $products->product_code }} | {{ $products->product_size }} </option>#endforeach</select></div></div></td><td class="col-sm-3"><input type="text" name="price" id="productPrice" class="form-control" placeholder="price" disabled/></td>
My select and text fields on which I am performing this operation.
enter image description here
enter image description here
As I am not getting any value in response when I try to print it on alert.
I just want to have a response from laravel function. If anyone can highlight what is wrong. That would be really helpful.

How to pass an array from Laravel controller to Vue.js component

I want to pass an array called hours from a Laravel controller to a Vue.js component but for some reason it's not working.
As you can see in the following code, I am trying to create dynamically the options inside a select that is located inside a component but for some reasons I see no options in that array.
In the view I created a <p> to check that the returning array from controller be correct and finally it is correct because in the view I am able to see the second value of the array.
But for some reasons I cannot visualize the values of the array inside the component.
This is my controller code:
$open_at = '00:00';
$close_at = '23:45';
$time = new DateTime($open_at);
$close = new DateTime($close_at);
while ($time < $close) {
$hours[] = $time->format('H:i');
$time->modify('+15 minutes');
}
return view('create')->with('hours', $hours);
This is my view code:
#extends('layouts.app')
#section('content')
<div class="container">
<div id="app">
<create-form :hours="hours"></create-form>
</div>
<p>
{{ $hours[1] }}
</p>
</div>
#endsection
This is code inside the template component:
<div class="form-group">
<label for="start">Start Hour:</label>
<select class="form-control" id="start">
<option v-for="hour in hours" :key="hour.id">
{{ hour }}
</option>
</select>
</div>
This is the my export_default:
export default {
props: ['hours[]'],
mounted() {
console.log('Component mounted.');
this.loadUsers();
this.loadRooms();
},
data: function() {
return {
users: [],
rooms: []
}
},
methods: {
loadUsers: function() {
axios.get('api/users')
.then((response) => {
this.users = response.data.data;
})
.catch(function(error) {
alert('noviva');
console.log(error);
});
},
loadRooms: function() {
axios.get('api/rooms')
.then((response) => {
this.rooms = response.data.data;
})
.catch(function(error) {
alert('noviva');
console.log(error);
});
}
}
}
I visualize the following warning in the console:
Property or method "hours" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
Can help?
The definition of your props property doesn't match the name by which $hours is passed to the component
props: ['hours'],
And again you do not have id on hours the way you are constructing the hours[] in controller so the template will give another error when you try :key="hour.id
You must either construct the hours array in controller such that it has an id for every entry or (not recommended) you can use the index in the v-for loop as key
<div class="form-group">
<label for="start">Start Hour:</label>
<select class="form-control" id="start">
<option v-for="(hour, index) in hours" :key="index">
{{ hour }}
</option>
</select>
</div>
PHP arrays and objects can be passed to javascript/vue as props by json encoding them
//Using the blade helper #json
<div id="app">
<create-form :hours='#json($hours)'></create-form>
</div>
//OR using json_encode()
<div id="app">
<create-form :hours="{{ json_encode($hours) }}"></create-form>
</div>

Getting value from (select2) to another filed input laravel

I have select2 field input. After using select2 its will showing new column and data from this input selected.
I have referenced like this link. So after I using select2, this value will show in a new column. But I don't know how to catch this data. I am using Laravel and this is my controller and view:
Controller
$collection = Alat::get(['nama_alat','no_inventaris','status_alat','id']);
foreach ($collection as $item) {
$inven[$item->id] = $item->no_inventaris.'-'.$item->nama_alat;
}
This is will shown in columns no_inventaris and nama_alat in the select2. But in the $collection, I have status_alat, this data is what I need to display in another column.
This is my view:
// This is form Select2
<div class="form-group">
<label>Pilih Inventaris</label>
<select class="form-control select2bs4" name="alat_id" id="alat_id" style="width: 100%;" aria-hidden="true" onchange="Show()">
<option value=""></option>
#foreach($inven as $id => $item )
<option value="{{ $id }}">{{ $item }} </option>
#endforeach
</select>
</div>
// This is form what i need to show another value
<div class="form-group" id="divid" style="display:none">
<label class="control-label" for="title">Kondisi Alat Sekarang:</label>
<input type="text" name="" class="form-control" id="value" data-error="Please enter title." readonly />
<div class="help-block with-errors"></div>
</div>
Here's my Javascript:
<script>
function Show()
{
var fieldValue = $('#alat_id').val();
if(fieldValue == "")
{
document.getElementById("divid").style.display = 'none';
}
else{
document.getElementById("divid").style.display = 'inline'
}
}
</script>
This data I need to catch in the controller $collection as status_alat. How can I catch this data after input the select2 and showing in the new column? This column is shown, but I don't know how to catch this data. Sorry for my bad English
The best solution should be using ajax, it is quite complicated to access a PHP collection variable inside a javascript. If it were me, I would create a function that fetch the selected select2 data by its id. This is my example code :
<script>
function select2Changed()
{
var alat_id = $('#alat_id').val();
if(fieldValue == ""){
document.getElementById("divid").style.display = 'none';
} else{
document.getElementById("divid").style.display = 'inline';
$.ajax({url: "[url]/get-alat-status/"+alat_id, success: function(result){
document.getElementById("value").value = result;
}});
}
}
</script>

Laravel Ajax dropdown example

can someone please share working example of laravel ajax dropdown. there are so many examples about dependable dropdown, but i want simple dropdown of only one column, i have two tables teacher and nation, when teacher profile is open i want dropdown of nationality using ajax.
i have done it without ajax, but i don't know how to do with ajax.
without ajax:
<select name="nation_id" class="custom-select" >
<option selected value=" ">Choose...</option>
#foreach($nations as $nations)
<option value="{{#$nation_id}}" {{#$teacher->nation_id== $nations->id ? 'selected' : ''}} >{{#$nations->nation}}</option>
#endforeach
Controller:
$nations = nation::all();
<select class="form-control" name="nation_id" id="nation_id">
<option value="">Select nation</option>
#foreach($nations as $nation)
<option value="{{ $nation->nation_id }}">{{ $nation->nation_name }} </option>
#endforeach
</select>
<select class="form-control" name="teacher" id="teacher">
</select>
now the ajax code:
<script type="text/javascript">
$('#nation_id).change(function(){
var nid = $(this).val();
if(nid){
$.ajax({
type:"get",
url:"{{url('/getTeacher)}}/"+nid,
success:function(res)
{
if(res)
{
$("#teacher").empty();
$("#state").append('<option>Select Teacher</option>');
$.each(res,function(key,value){
$("#teacher").append('<option value="'+key+'">'+value+'</option>');
});
}
}
});
}
});
</script>
now in controller file;
public function getTeacher($id)
{
$states = DB::table("teachers")
->where("nation_id",$id)
->pluck("teacher_name","teacher_id");
return response()->json($teachers);
}
And last for route file:
Route::get('/getTeacher/{id}','TeachersController#getTeacher');
Hope this will work..
Good Luck...
Create a route for your method which will fetch all the nations-
Route::get('nations-list', 'YourController#method');
Create a method in your controller for the above route-
public function method()
{
$nations = Nation::all()->pluck('nation', 'id');
return response()->json($nations)
}
Add a select box like this in your HTML-
<select id="nation_id" name="nation_id"></select>
If you want to auto select the option based on a variable then you can do this-
<input type="hidden" name="teacher_nation_id" id="teacher_nation_id" value="{{ $teacher->nation_id ?? '' }}">
And then add this script in your HTML to fetch the nation list on page load-
<script>
$(document).ready(function($){
$.get('nations-list', function(data) {
let teacher_nation_id = $('#teacher_nation_id').val();
let nations = $('#nation_id');
nations.empty();
$.each(data, function(key, value) {
nations.append("<option value='"+ key +"'>" + value + "</option>");
});
nations.val(teacher_nation_id); // This will select the default value
});
});
</script>

Laravel 5.2 Vue.js computed doesn't work

How can I show objects returned by vue?
The provinces are Ok, But cities v-for doesn't work.
This is My Blade :
<select v-model="ProvinceModel" name="province" id="province" class="border-radius-0 form-control padding-y-0">
<option v-for="province in provinces" value="#{{ province.id }}"> #{{ province.name }} </option>
</select>
<select name="city" id="city" class="border-radius-0 form-control padding-y-0">
<option v-for="city in cities" value="#{{ city.id }}"> #{{ city.name }} </option>
</select>
This is my scripts
new Vue({
el: '#vue',
methods: {
fetchProvinces: function () {
this.$http.get('{{url('api/provinces')}}').then(function (provinces) {
this.$set('provinces', provinces.data)
});
}
},
computed: {
cities() {
this.$http.get("{{url('api/cities')}}/" + this.ProvinceModel).then(function (cities) {
console.log(cities.data);
this.$set('cities', cities.data)
});
}
},
ready: function () {
this.fetchProvinces()
},
});
And route
Route::get('cities/{provinces_id}', function ($id = 8) {
return \App\province::find($id)->cities()->get();
})->where('id', '[0-9]+');
Computed functions should return something, and sycnchronously. You're not returning anything.
You're also attempting to set this.cities as data, but this.cities is already a computed function. One's gonna override the other and cause inconsistent/confusing behavior.
Fetching cities asynchronously should be a method, just like your fetchProvinces method. You should fetch them when ProvinceModel is changed, which you can probably do via a #change event on the selector, or a Vue watcher on the value of ProvinceModel.
Side note: Generally, it's best to define your data items in the component's data parameter. If you check your JS console, Vue's likely throwing warnings about that.
Vuejs not flexible, you can get value through jquery, pass it to control. Element by ref method is good.
Vuejs can not wait for worker,when it get busy, so naturally variable when it is not in value show undefined.

Resources