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();
});
}
Related
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));
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>
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!
Hi i am making my project in the asp.net,
basic expected behaviour -- fill form name , select master module(dropdown), select sub modules(dropdown), ajax passes id of submodule dropdown, create(submit).. it will submit all values,
now code is behaves---- fill form name, select master and submodule, while selecting submodule from second dropdown is calling the ajax call, and create action is called, so the form name and masterID(that is extracted from first dropdown) gone blank... so i need to prevent the ajax call to call the controller
Myform in razor view
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Form</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FormName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FormName)
#Html.ValidationMessageFor(model => model.FormName)
</div>
<select id="State" name="state"></select><br />
<p>
<input id="sbmt" type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
My ajax call
$('#State').change(function () {
var a = $('#State').val();
var token = $('[name=__RequestVerificationToken]').val();
$.ajax({
url: "/form/create",
type: "POST",
data: { __RequestVerificationToken: token, 'SubID': a }
});
});
My controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Form form, int SubID)
{
if (ModelState.IsValid)
{
form.CreatedBy = 1;
form.SubId = SubID;
form.CreatedDate = DateTime.Now;
form.ModifyBy = 1;
form.ModifyDate = DateTime.Now;
form.IsActive = true;
form.IsDeleted = false;
db.Forms.Add(form);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.MasterID = new SelectList(db.Departments, "MasterId", "ModuleName", form.MasterID);
return View(form);
}
From the comments, you want to be able to post back the value of the selected state when you submit the form. The best approach would be to use a view model that includes the property SubId and bind your dropdownlist to that property (the SubId parameter in you POST method is then not necessary.
#Html.DropDownListFor(m => m.SubId, ....)
The alternative is to rename the control to SubId so it matches the parameter in your POST method
<select name="SubId" ...>
and delete the unnecessary javascript function $('#State').change(function () {...
I guess the easiest way for you would be to use MVC's ajax helpers. Change your form like so:
#using (Ajax.BeginForm("Create", "Home", null, new AjaxOptions { OnSuccess = "OnSuccess" }))
{
...
And afterwards add a javascript handler for onsuccess
function OnSuccess(){
alert("success");
}
This is barely functional code just to get you started.
PS. Make sure you add a reference to ~/Scripts/jquery.unobtrusive-ajax.min.js
Hi.
I`m trying to integrate Spring MVC with bootstrap modal - having filter region I want to select item from modal form and set it to the filter via jQuery ajax.
Main idea: show modal popup when user clicks on button to select item, this popup must allow user to search in the list (ajax) and click submit button to select item.
To refresh popup data I return ModelAndView with filtered list of items, and then, if user click 'submit', I send selected index to server where I can get id by index in the list, but ModelMap (and Model too) does not contain this list.
What am I doing wrong?
I have following structure:
Main JSP's:
main.jsp
....
<div class="override side-nav raised" id="filter">
<jsp:include page="filter.jsp"/>
</div>
....
filter.jsp
<f:form method="get" cssClass="search" action="${pageContext.request.contextPath}/search"
commandName="searchModel">
....
<div class="form-group">
<label class="control-label" for="searchDpt"><s:message code="label.department"/></label>
<div class="controls">
<f:input id="searchDpt" cssClass="form-control col-sm-8" cssStyle="width: 75%;" path="dept"/>
<button type="button" id="dsb_filter" class="btn btn-default col-sm-3" data-toggle="modal" data-target="#selectDeptModal"><span
class="glyphicon glyphicon-list"></span></button>
</div>
</div>
....
</f:form>
Modal window for item selection:
modal.jsp
<div id="selectDeptModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"
style="display: none;">
....
<div class="table-responsive" id="dept_table">
<jsp:include page="deptTable.jsp"/>
</div>
<button type="button" class="btn btn-default" onclick="selectDept();"><s:message code="label.select"/></button>
</div>
And jQuery code for ajax search and POSTing data:
function search() {
$.ajax({
url: "departments",
data: "name=" + $("#dpt_filter").val(),
success: function (response) {
$("#dept_table").html(response);
}
});
}
function getSelectedIndex() {
var table = $("#dept_table").find("table")
var selectedRows = table.find("tr.selected-row");
var elements = [];
if (selectedRows.length > 0) {
table.find("tr.selected-row td.index-col").each(function (i, el) {
elements.push(parseInt($(el).text()));
});
}
return elements;
}
function selectDept() {
var selected = getSelectedIndex()[0];
if (selected == undefined || selected.length == 0) {
$("#no_selection").removeClass("hidden");
return;
}
$('#selectDeptModal').modal('hide');
$.ajax({
type: "POST",
url: "deptSelected",
data: { index: selected},
success: function (response) {
$("#filter").html(response);
}
});
}
Controller methods for handling JavaScript:
#RequestMapping(method = RequestMethod.GET, value = "departments")
public ModelAndView getDepartments(#RequestParam(required = false) String name, Model model) {
LOG.debug(name);
model.addAttribute("dpts", departmentService.filterByName(name));
return new ModelAndView("selectDeptDialog/deptTable");
}
#RequestMapping(method = RequestMethod.POST, value = "deptSelected")
public ModelAndView deptSelected(#RequestParam Long index, ModelMap model) {
SearchModel sModel = (SearchModel) model.get("searchModel");
sModel.setDept(index);
Object depts = model.get("dpts");// = null
return new ModelAndView("filter", model);
}
But when getting list of departments in deptSelected it is null.
Thanks in advance!
ModelMaps don't keep their state between requests. You may, however, be able to get some of the same result using a #SessionAttribute, however, I don't think that will get your desired result, either.