Thymeleaf + Javascript select option change - javascript

I want to select a list of elements, based on selection I call a function api that give me with a list of data, to insert in a second selection.
The call works fine, I try to alert the result, but I need to change the value of second selection and I fail.
I'm using select2.
This is my THYMELEAF page:
<form id="updateD" data-parsley-validate class="rounded px-2 py-2 mt-3" th:action="#{/ditta/upd}" th:method="POST" th:object="${ditta}">
<div class="row" th:if="*{codice==null or codice.isEmpty()}">
<div th:replace="~{/tags/input :: inputNorm(indirizzo, *{indirizzo}, text, true, '')}"></div>
<div th:replace="~{/tags/select :: selectValue(city, *{city}, ${cities}, false)}"></div>
<div th:replace="~{/tags/select :: selectValue(zip, *{zipCode}, ${zips}, false)}"></div>
</div>
</form>
JS code:
<script type="text/javascript" th:inline="javascript">
/*<![CDATA[*/
var token = $('#_csrf').attr('content');
var header = $('#_csrf_header').attr('content');
$(function(){
$('#city').change(function(){
handleComune($('#city').val());
});
$('.sel2').select2({
theme: "bootstrap"
});
});
function handleComune(city){
$.ajax({
type: 'GET',
url:[[ #{'/api/extractZip?city='}]]+city,
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token);
},
success : function(data) {
$('#zips').val(data);
alert(data);
}
});
}
/*]]>*/
</script>
Based on the choice of the city I extract a zipcode list.
The fragments of the select is the follow:
<div th:fragment="selectValue(name, selected, values, required)" class="col form-group" th:switch="${required}">
<label th:text="${required} ? #{${name}}+'*' : #{${name}}" th:for="${name}" class="control-label"></label>
<div class="col form-group">
<select th:name="${name}" th:id="${name}" class="form-control sel2" th:required="${required}">
<option value=""></option>
<th:block th:each="v : ${values}">
<option th:value="${v}" th:text="${v}" th:selected="${selected} eq ${v}"></option>
</th:block>
</select>
</div>
</div>
The $('#zips').val(data); not change.
What's wrong?

If you are returning a Thymeleaf fragment from your Controller, then you will be getting a html code. So, val() won't work. You need to use append(data). Now I don't see your Controller or where the element $('#zips') is at, so I am assuming that is what you are sending.
If you are sending a list of entities, then you would need to use $.each(data, function() { // Do something }); to add each entity as an option in your select.

After several attempts I solved this way:
function handleComune(city){
$.ajax({
type: 'GET',
url:[[ #{'/api/extractZip?city='}]]+city,
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token);
},
success : function(data) {
$('#zip').empty();
var arrayCap = data.toString().split(",");
for (a in arrayCap ) {
var newOption = new Option(data[a], data[a], false, false);
$('#zip').append(newOption).trigger('change');
}
}
});
}
I need to split my data, because the response is string of data separated by commas.
Now it works perfectly!

Related

How do I access the list element that is appended after html was created

I am adding a list element using the append function in jQuery, how do I reference this on my onclick function? Below is my code
$(function() {
let $movies = $('#showList')
let $m = $('#show')
$.ajax({
method: 'GET',
url: 'http://api.tvmaze.com/shows',
success: function(movies) {
$.each(movies, function(i, movie) {
$movies.append('<li id="list">' + movie.name + '</li>')
})
}
})
});
$('#list').on('click', 'a', function(event) {
console.log('here');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<h1 id='bruh'>TV Shows</h1>
<ul id="showList"></ul>
<div id="show"></div>
<form id="searchForm">
<input type="text" id="search_term">
<label for="text">Search</label>
<button>Submit</button>
</form>
<a id="homelink" href="#" hidden>Back to All Shows</a>
There is no message in console when I click on the link.
The attribute id should unique in a document, you can use class instead.
Try $('body').on('click', '.list a', function(event){....
Demo:
$(function() {
let $movies = $('#showList')
let $m = $('#show')
$.ajax({
method: 'GET',
url: 'http://api.tvmaze.com/shows',
success: function(movies) {
$.each(movies, function(i, movie) {
$movies.append('<li class="list">' + movie.name + '</li>')
})
}
})
});
$('body').on('click', '.list a', function(event) {
console.log('here');
event.preventDefault();//stay on the same page
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<h1 id='bruh'>TV Shows</h1>
<ul id="showList"></ul>
<div id="show"></div>
<form id="searchForm">
<input type="text" id="search_term">
<label for="text">Search</label>
<button>Submit</button>
</form>
<a id="homelink" href="#" hidden>Back to All Shows</a>
You can give the appended divs a class then call them by it like this :
$movies.append('<li class="movie" id="list">'+ movie.name +'</li>');
//then later
let movies = $('.movie');
console.log(movies);
but if you meant to execute this script right after the ajax request
then you should call a function in the ajax success since ajax is async and the moment you execute a jquery select , the elements are still not there because ajax takes more time to retrieve the data put it in the document...
you can make a getMovies() to execute there like this:
//declare this outside the ajax
let movies = []
function getMovies(){
return $('.movie');
}
then
//ajax
success: function(){
movies = getMovies(); //add this line
}
there you go now you have movies stocked inside the 'movies' array
It seems you are running into a race condition of sorts. You are building the list using an asynchronous ajax request to an API. Before that completes, you are then attaching the Javascript code to trigger an event.
What you need to do is add the callback inside of the ready block after the list data is retrieved and the list is created.

How to manage dependent dropdown list in php when create and update method is managed in same page

I have written the code to display the dependent dropdown list in php, when I create the record it is working. What changes I need to do while updating the record as I want the create and update method in same page.
Here's the code for reference,
<div class="form-group row m-b-15">
<label class="col-md-4 col-sm-4 col-form-label" for="fin_is_fin_req">
Is Finance Required? * :
</label>
<div class="col-md-4 col-sm-4">
<select name="fin_is_fin_req" id="is_finance" class="form-control" autofocus>
<option value="">-Select Finance-</option>
<?php
$fin_req=array('Yes','No');
foreach ($fin_req as $list) {
if ($allotment->fin_is_fin_req==$list):?>
<option selected value="<?=$list?>">
<?php else:?>
</option>
<option value="<?=$list?>">
<?php endif;?>
<?=$list?></option>
<?php } ?
</select>
</div>
</div>
<div class="form-group row m-b-15 ">
<label class="col-md-4 col-sm-4 col-form-label" for="fin_stage">
Finance Stage * :
</label>
<div class="col-md-4 col-sm-4">
<select name="fin_stage" id="stages" class="form-control">
</select>
</div>
</div>
$(document).ready(function () {
$('#is_finance').on('change', function () {
var finance_value = $("#is_finance").val();
$.ajax({
url: 'ajax/loadstages.php',
method: 'post',
data: 'finance_value=' + finance_value
}).done(function (stages) {
stages = JSON.parse(stages);
stages.forEach(function (stage) {
$('#stages').append('<option value=' + stage.id + '>' + stage.stage_name + '</option>')
})
})
});
});
loadstages.php
if (isset($_POST['finance_value'])) {
if ($_POST['finance_value'] == "Yes") {
$stages = FinStage::select('*', 'state=1');
//select(rows,where)
} else {
$stages = FinStage::select('*', 'state=0');
}
}
echo
json_encode($stages);
//screenshot during insert
//screenshot during update
If I understand you, you are saying that when you open the page, you want that page to show that second select, if the first dropdown has a value saved to it already? If that is the case, you can load it initially with PHP initially or you can adjust your JS to load the first dropdown at page ready:
<script>
// I might consider making this an object for reuse
class FormController
{
fetch(data, page, success)
{
$.ajax({
// Presumably all your ajax files are in this directory
url: `ajax/${page}.php`,
method: 'post',
data: data
}).done(r => {
// If your ajax pages send headers for Content-Type: application/json,
// you can skip the JSON parse if you want to
success(JSON.parse(r));
});
}
}
// If you extend the main form, you can use the ajax
class Finance extends FormController
{
getStages()
{
// Fetch the value from dropdown
const finance_value = $("#is_finance").val();
// Fetch the value of the first dropdown
super.fetch({ finance_value }, 'loadstages', r => {
// Do your append loop from the return
r.forEach(stage => {
$('#stages').append(`<option value=${stage.id}>${stage.stage_name}</option>`);
});
});
}
}
// Document ready
$(() => {
// Create the financial form
var FinForm = new Finance();
// Fetch the select value on page load
FinForm.getStates();
// On change, fetch the value again
$('#is_finance').on('change', () => {
FinForm.getStates();
});
});
</script>
Your PHP probably should be more like:
<?php
# Consider using this header
// header('Content-Type: application/json');
# Set a default, makes sure the json doesn't throw a warning if the condition
# is not set for whatever reason
$stage = [];
if (isset($_POST['finance_value'])) {
$finAffirm = ($_POST['finance_value'] == "Yes");
# You can just do this in one line so there is no duplication
$stages = FinStage::select('*', 'state='.(($finAffirm)? '1' : '0'));
# Fetch only on the affirmative
if ($finAffirm) {
//select(rows,where)
}
}
# It's safer just to exit here
die(json_encode($stages));

How to pass a value back from Java controller to Thymeleaf front-end?

I am building an application that when the user selects a value from a select tag, then the user is prompted with another select tag with corresponding data. I achieve this by grabbing the data from the first field and using Ajax to pass it back to my controller and then compute it and send back new data. However, I am having difficulty sending the object back. I am not sure if I am doing it right. I tried using model.addAttribute() but I believe I set it up wrong.
Tools:
Java
Spring Boot
Thymeleaf
Bootstrap
Controller:
#RequestMapping(value="/ajax/searchUserProfiles.html",method=RequestMethod.POST)
public #ResponseBody void getSearchUserProfiles(#RequestBody TestApp mTestApp, HttpServletRequest request) {
String val = mTestApp.getDtoTiername();
List<String> t1 = new ArrayList<String>();
for(TestApp temp: exampleAry) {
if(temp.getDtoTiername().equalsIgnoreCase(val)) {
t1.add(temp.getDtoSystem());
}
}
//I want to return t1 to my select tag in my front-end
}
Ajax:
function searchText() {
var search = {
"dtoTiername" : "Web"
}
$.ajax({
type: "POST",
contentType : 'application/json; charset=utf-8',
dataType : 'json',
url: "/ajax/searchUserProfiles.html",
data: JSON.stringify(search), // Note it is important
success :function(result) {
// do what ever you want with data
}
});
}
HTML:
<div class="col col-lg-9 search-bar">
<div class="form-group">
<label>Tier:</label>
<select class="js-example-basic-single" th:field="*{ary4}" id="selectData1">
<option value=""></option>
<option th:each="ary4 : ${ary4}"
th:value="${ary4.dtoTiername}"
th:text="${ary4.dtoTiername}"/>
</select>
<div class="text-right">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col col-lg-9 search-bar">
<div class="form-group">
<label>Type:</label>
<select class="js-example-basic-single" th:field="*{t1}" id="selectData2">
<option value=""></option>
<option th:each="t1 : ${t1}"
th:value="${t1.dtoTiername}"
th:text="${t1.dtoTiername}"/>
</select>
<div class="text-right">
</div>
</div>
</div>
</div>
So far dropdown is concerned, it contains both option value and option text. But if you want to use both values same that can also be done. For it,
modify your controller code as
#RequestMapping(value="/ajax/searchUserProfiles.html",method= RequestMethod.POST)
public #ResponseBody List<String> getSearchUserProfiles(#RequestBody String mTestApp, HttpServletRequest request) {
List<String> t1 = new ArrayList<>();
t1.add("AAAA"); // i added sample text, however you can fetch values from dao as well
t1.add("BBBBB");
return t1;
}
Update your javascript code as
function searchText() {
var search = {
"dtoTiername" : "Web"
}
$.ajax({
type: "POST",
contentType : 'application/json; charset=utf-8',
dataType : 'json',
url: "/ajax/searchUserProfiles.html",
data: JSON.stringify(search),
success :function(result) {
$.each(result,function(i,obj)
{
var div_data="<option value="+obj+">"+obj+"</option>"; //this would modify if you return map from java instead of list
$(div_data).appendTo('#testing'); //append new dynamically generated option to element having id "testing"
});
}
});
}
Please ensure that your have html select option with id specified in above script as below
<select id="testing"></select>
With above code, if you call js function searchText multiple times, multiple copies of option will be appended to the drop-down. Now it will be your use case to empty\append\block in such scenario.
Also the controller code should go in class annotated with #RestController, not in #Controller class which returns ModelAndView
EDIT : use html page as
<html>
<script>
// Here paste script provided
</script>
<body>
<input type="button" value="test" onclick="searchText()" />
<select id="testing"></select>
</body>
</html>

Spring Boot Ajax populate a select options dynamically

Hello every one i have the following question ,i have a select tag that i want to populate with a list objects Here some code to explain:
#RequestMapping(value="/getAllIndustries")
#ResponseBody
public List<IndustryModel>getAllIndustries()
{
return generalSettingMerchantLookUpService.getBusinessNature(Constant.MERCHANT);
}
This return a list of industries models ,the following is the java script that i am using
function industryAjax()
{
alert("i am here")
var businessNatureId= $("#industrySelect option:selected").val();
$.ajax({
url : getContextPath() + "/getAllIndustries",
type : "get",
success : function(response) {
$('#industrySelect').empty();
$('#industrySelect').append($('<option>', {
value: 0,
text: 'Select'
}));
for (item in response) {
$('#industrySelect').append($('<option>', {
value: response[item].industryId,
text: response[item].industryInEnglish
}));
}
},
error : function(e) {
// alert("Submit failed" + JSON.stringify(e));
}
});
}
And here i my html
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
<label>Industry</label> <select class="form-control m-b"
id="industrySelect" name="industryId" onchange="industryChange();">
<option value="0">Choose Industry</option></select>
<p id="chooseIndustry" style="color: red;">please choose a valid industry</p>
</div>
So how can i display the list of industries that i get from controller in the html ,Best Regards
It might be the case that this method is not getting invoked because initially your dropdown does not contain any element. So there will not be any onchange event fired.
Try using below implementation:
$(document).on('click', "#industrySelect", function(event) {
industryAjax();
});

AJAX Call is Null

I have an AJAX call in the View, and its having difficulties pulling from DB to the populate the drop down list. The DB has been populated. Inside the browser console I can see it has found the correct ID, but I receive the following error:
"Failed to load resource: the server responded with a status of 500 (Internal Server Error)"
Clicking this link take me to a server error:
"The parameters dictionary contains a null entry for parameter 'serviceId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.JsonResult GetServiceDetails(Int32)' in 'YardLad.Controllers.ServiceController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters"
Here is the code I have so far:
AJAX Call:
function GetServices() {
$.ajax({
type: "GET",
url: "/Service/GetServices",
data: { serviceAreaId: $('#ServiceAreaId').val() },
datatype: 'JSONP',
async: true
})
.done(function (services) {
$('#ServiceId').empty(); //commenting this line gets the field to never populate with nulls, leaving the
//"loading services..." there permanently, also brings up the request a contractor button.
$.each(services, function (i, service) { //the i is the index being passed to it... service is the value
$("#ServiceId").append(
$('<option/>')
.attr('value', this.ServiceId)
.text(this.Name)
);
});
GetServiceDescription();
});
}
Controller:
//
// GET: Service/GetServices/
public JsonResult GetServices(int serviceAreaId)
{
// selected service area
var selectedServiceArea = db.ServiceAreas.Where(sa => sa.ServiceAreaId == serviceAreaId).SingleOrDefault();
var currentDate = DateTime.Now.AddHours(2);
// list of contractors that are listed (and not expired) and have at least one contractor service added
var contractors = db.Contractors.Where(c => c.IsActive == true).Where(c => c.ExpiresOn.Value >= currentDate).Where(c => c.ContractorServices.Count >= 1).ToList();
HashSet<int> contractorIds = new HashSet<int>(contractors.Select(x => x.ContractorId));
// filter list of contractors to include only those who offer services in the selected service area
var primary = db.Contractors.ToList().FindAll(x => contractorIds.Contains(x.ContractorId))
.Where(c => c.ServiceAreaId == serviceAreaId).Distinct().ToList();
var secondary = db.ContractorServiceAreas.ToList().FindAll(x => contractorIds.Contains(x.ContractorId))
.Where(csa => csa.ServiceAreaId == serviceAreaId).Select(x => x.Contractor).Distinct().ToList();
foreach (var contractor in secondary)
{
if (primary.Contains(contractor) == false)
{
primary.Add(contractor);
}
}
// only return services in the selected service area that contractors have added
HashSet<int> filteredContractorIds = new HashSet<int>(primary.Select(x => x.ContractorId));
var services = db.ContractorServices.ToList().FindAll(x => filteredContractorIds.Contains(x.ContractorId))
.Select(x => x.Service).Distinct().OrderBy(s => s.ServiceCategory.Name).ThenBy(s => s.Name)
.Select(x => new {
ServiceId = x.ServiceId,
Name = x.Name
});
return this.Json(services, JsonRequestBehavior.AllowGet);
}
//
// GET: Service/GetServiceDetails/1
public JsonResult GetServiceDetails(int serviceId)
{
var details = db.Services.Where(s => s.ServiceId == serviceId)
.Select(x => new
{
Description = x.Description,
BasePrice = x.BasePrice
});
return this.Json(details, JsonRequestBehavior.AllowGet);
}
.cshtml
#using (Html.BeginForm("RequestService", "Service", FormMethod.Post))
{
<div class="two-third" style="margin-right: 0;">
<div id="requestService" style="overflow: auto">
<h2>Request a Service</h2>
#Html.ValidationSummary(true)
<div class="display-label">Select a State</div>
<div class="editor-field">
<select id="StateId" name="StateId">
<option value="0">loading states...</option>
</select>
</div>
<div class="display-label">Select a Service Area</div>
<div class="editor-field">
<select id="ServiceAreaId" name="ServiceAreaId">
<option value="0">loading service areas...</option>
</select>
</div>
<div class="display-label">Select a Service</div>
<div class="editor-field">
<select id="ServiceId" name="ServiceId">
<option value="0">loading services...</option>
</select>
</div>
<div class="selectedService" style="display: none;">
<div class="display-label">Service Description</div>
<div class="editor-field" id="ServiceDescription"></div>
<div class="serviceOptions" id="ServiceOptions"></div>
<div style="text-align: right;">
<input type="submit" id="SubmitRequest" value="Select a Contractor" />
</div>
</div>
</div>
</div>
<div class="one-third" style="float: right; margin-top: 0;">
<div id="summary">
<h3>Yard Lad Service Estimate</h3>
<p>This is an initial Yard Lad estimate. The end total may be higher or lower based upon the contractor you choose,
as well as the contractors on-site assessment. Payment adjustments are easy to make on site or online after the
service has been completed.</p>
<div style="font-weight: bold;">Subtotal: $<span class="subtotal">0.00</span></div>
</div>
</div>
<input type="hidden" class="stateId" name="StateId" value="0" />
<input type="hidden" class="serviceAreaId" name="ServiceAreaId" value="0" />
<input type="hidden" class="serviceId" name="ServiceId" value="0" />
After changing the Ajax call "url" The server error has slightly changed. I provided a picture of the update below:
Server Error Image
When your server receives the HTTP Get request, it is expecting an Id in the query string inside of the url(ex Service/GetServices/1), which in in request header. Your Id is actually in the request body not in url. That is why your server code is complaining. To make this work, you can change your JavaScript code to
function GetServices() {
$.ajax({
type: "GET",
url: "/Service/GetServices/?serviceId=" + $('#ServiceAreaId').val(),
dataType: "jsonp",
async: true
})
.done(function (services) {
$('#ServiceId').empty(); //commenting this line gets the field to never populate with nulls, leaving the
//"loading services..." there permanently, also brings up the request a contractor button.
$.each(services, function (i, service) { //the i is the index being passed to it... service is the value
$("#ServiceId").append(
$('<option/>')
.attr('value', this.ServiceId)
.text(this.Name)
);
});
GetServiceDescription();
});
}

Categories

Resources