Serialized select2 value not available in the controller of asp.net mvc - javascript

I have a view which has multiple controls inside a div which I want to serialize and pass it to the controller via AJAX.The SchoolType field in the view is a select2 multi select dropdown.
Model :
public class SchoolModel
{
public string StudentName{ get; set; }
public List<string> SchoolType{ get; set; }
public List<SelectListItem> SchoolTypeList{ get; set; }
}
View :
<div id="divSchool">
<div class="row">
<div class="col-md-12">
<div class="col-md-6">
<div class="form-group">
<label asp-for="SchoolType" class="col-md-3 control-label">School Type</label>
<div class="col-md-9">
<select asp-for="SchoolType" asp-items="Model.SchoolTypeList" class="form-control medium"></select>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label asp-for="StudentName" class="col-md-3 control-label">Student Name</label>
<div class="col-md-9">
<input asp-for="StudentName" class="form-control" />
</div>
</div>
</div>
</div>
</div>
</div>
Controller:
[HttpPost]
public ActionResult Index(SchoolModel model)
{
}
My JS Code:
$('#SchoolType').select2({
placeholder: "Filter by school"
}
$("document").on("click", ".btnCheck", function () {
var model = $('#divSchool').find('select, textarea,input').serialize();
$.ajax({
url: "/Student/Index",
type: 'POST',
data: { model: model },
cache: true,
async: true,
}).done(function (result) {
}).fail(function (error) {
})
});
However the serialized div appears something like
model = "StudentName=Test&SchoolType=1b&SchoolType=26a"
While these values are right on the client side, on AJAX the StudentName value appears fine whereas the SchoolType value appears as null on the server side in the controller. How do I fix this?
Possible issue: Because the SchoolType value is a List of string, it's not getting mapped to the individual strings.
EDIT 1: I tried to change the div into form but the same issue persists.
EDIT 2: This issue is handled in PHP by changing the select name. This answer shows an example.

While I am not sure you can manipulate the serialization part; you can get that structure manually.
You can serialize other controls like how you are doing using serialize() and assign it to model. And for the Select2, do it this way and add it to the model.
model = {};
$(".select2").each(function () {
var selectedValue = $(this).val();
if (selectedValue != null) {
model[this.name] = selectedValue;
}
});
This will give you the structure you need.

Related

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>

selected multiple from from the multi select column in the view file after calling the function

How to pass the value from multiselect column from view into controller Get Method. I am trying to pass the value from dropdownDepot into the controller
argument List Department. Please help
public class ReportViewModel
{
public int DepotNo { get; set; }
public List<int> Department { get; set; }
}
[HttpGet]
public IActionResult EmployeeView(ReportViewModel report, List<int> Department,int DepotNo = 0)
{
List<EmployeeWorkTimeSettingsModel> log = new List<EmployeeWorkTimeSettingsModel>();
log = ReportService.GetDepotEmployeeWorkHrsReport(connectionSettings, (HttpContext.Session.GetInt32("UserID") ?? 0).ToString(),
String.Join(",", report.DepotNo), String.Join(",", report.Department));
ViewBag.EmpviewReport = log;
}
<div class="controls col-sm-9">
<select asp-for="DepotNo" class="form-control" id="dropdownDepot" asp-items="#ViewBag.UserDepots" onchange="FillEmployee()" data-role="select"></select>
<span asp-validation-for="DepotNo" class="text-danger"></span>
</div>
<div class="controls col-sm-9">
<select id="dropdownDepartment" class="form-control" asp-for="Department" asp-items="#ViewBag.UserDepartments" onchange="FillEmployee()"
data-role="select"></select>
</div>
$('#dropdownDepartment').multiselect({
columns: 1,
placeholder: 'Select Department',
includeSelectAllOption: true,
maxHeight: 300
});
function FillEmployee() {
var href = '/Report/EmployeeView?depot=' + $("#dropdownDepot option:selected").val() + '&department=' + Get the Value from multiselect departments;
$("#lnkFilter").attr('href', href);
$("#lnkFilter").click();
}
You could use ajax to pass all your data to controller when you select
function FillEmployee() {
$.ajax({
type: 'GET',
url: '/Report/EmployeeView?DepotNo=' + $('#dropdownDepot').val() + '&department=' + $('#dropdownDepartment').val().join('&department='),
success: function (result) {
console.log('Data received: ');
console.log(result);
}
});
}
Action:
[HttpGet]
public IActionResult EmployeeView([FromQuery]List<int> Department, [FromQuery]int DepotNo)
You can use val() directly on the select element, without having to filter by the selected options first. When you do so on a select multiple, you get back an array of selected values:
$('#dropdownDepot').val() // single selected value
And
$('dropdownDepartment').val() // array of selected values
In order for the array to bind properly, you'll need to actually repeat the department param for each selected value:
&department=value1&department=value2&department=value3
Something like the following should work for that:
'&department=' + $('#dropdownDepartment').val().join('&department=')

How to get Model value in javascript function and pass it to controller?

I have a Model on my CSHTML page that which I use this way:
#model Web.Models.Element
#using (Html.BeginForm("Search", "Company"))
{
#Html.HiddenFor(c => c.Person)
<div class="panel panel-default">
<div class="panel-heading"> Company Search</div>
<div class="panel-body collapse" id="screenCompanySearch">
<form id="formSearch" name="formSearch" method="post">
<fieldset style="margin: 20px;">
<legend>Search</legend>
<div class="row">
<div class="col-xs-2 col-sm-2">
#Html.Label("Company Name")
#Html.TextBoxFor(c => c.Company.Name, new { #class = "form-control" })
</div>
</fieldset>
</form>
</div>
</div>
My Javascript function is called by a button click this way:
$("#btnSearch").on("click", function() { searchCompany(); })'
In my JavaScript function I need to get this Model entirely loaded with the TextBoxFor values:
<script type="text/javascript">
function searchCompany() {
var data = $("#formSearch").serialize();
$.ajax({
url: "#(Url.Action("SearchCompany", "Company"))",
cache: false,
data: data,
type: "POST",
success: alert("sucesso!")
});
}
</script>
My Controller method is being loaded correctly, but the model passed in the Ajax "data" parameter is not filled with the TextBoxFor values.
This is my Controller ActionResult for the View:
public ActionResult Consulta()
{
Element model = new Element();
model.Person = new Person();
return View(model);
}
What is happening is that my Model is being instantiated on my Controller but the values from the TextBoxFor is not recorded on the properties of the Model.
How can I solve this? Thanks for now.
UPDATED
<div class="col-xs-2 col-sm-2">
#Html.Label("Person Name")
#Html.TextBoxFor(c => c.Person.Name, new { #class = "form-control" })
</div>
So, 'c' equals my Element object. When I reach the Controller Method "Search", the parameter Element passed via ajax call does not instantiate the Element.Person which gives me Person = null.
In my ActionResult I have:
Element model = new Element();
model.Person = new Person();
Element class:
public Element()
{
this.Contacts = new List<Contact>();
this.DataType = new DataType();
}
public int ID_Element { get; set; }
public int ID_ElementType { get; set; }
public virtual List<Contact> Contacts { get; set; }
public virtual DataType DataType { get; set; }
public virtual Person Person {get; set; }
Controller Action
[HttpPost]
public JsonResult SearchCompany(Element model)
{
...
}
The serialize method is not giving your the serialized version of the form because you have nested form tags.
The #using (Html.BeginForm("Search", "Company")) will create an outer form tag and you have your other form inside that, hence creating a nested form structure. Nested forms are invalid. You can have 2 forms in the same page, parallel to each other, not nested.
If you fix the nested form issue, the serialize method will give you valid string for you form inputs.
#using (Html.BeginForm("Search", "Company"))
{
<!-- Your other form -->
}
<form id="formSearch" name="formSearch" method="post">
<fieldset style="margin: 20px;">
<legend>Search</legend>
<div class="col-xs-2 col-sm-2">
#Html.Label("Company Name")
#Html.TextBoxFor(c => c.Company.Name, new { #class = "form-control" })
</div>
</fieldset>
</form>
Keep in mind that, the serialize method will give you the input element values of items inside this specific form. If you want to send some other data (ex : Id), you need to keep that in an input field inside this form.

MVC 5 Razor view template binding hide script to a model radiobutton

Currently have a custom EditorTemplate which dynamically populates based on the incomming Model () in the Razor page.
The aim is to be able to hide the individual div 'Sub_Text' in the editor template based on the radio value.
Model: Prime.cs
public class Prime{
public List<QuestionModel> Questions { get; set; }
}
Model: QuestionModel.cs
public class QuestionModel{
public int Id { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
public string SubText { get; set; }
}
Main View: _Reporting.cshtml
#model ViewModels.Prime
#for (int i = 0; i < Model.Questions.Count(); i++) //Dynamically generate and model bind database PolicyHolderKeyQuestions
{
#Html.EditorFor(x => x.Questions[i], "QuestionModel")
}
EditorTemplate: QuestionModel.cshtml
#model ViewModels.QuestionModel
#{
<div class="col-lg-2">
#Html.RadioButtonFor(Model => Model.Answer, YesNoNAOptions.Yes)
#Html.RadioButtonFor(Model => Model.Answer, YesNoNAOptions.No)
#Html.RadioButtonFor(Model => Model.Answer, YesNoNAOptions.NA)
</div>
<div class="col-lg-9">
<div class="row">
<p>
<strong>#Model.Question</strong>
</p>
</div>
<div class="row" name="**Sub_Text**"> #* **Hide Me!** *#
#Model.SubText
</div>
</div>
}
So far the closest idea I have found is to add a script something like this to the bottom of the template:
<script type="text/javascript">
$(':radio[name=Answer').change(function () {
// read the value of the selected radio
var value = $(this).val();
var doc
if (value == 1) {
$('#Sub_Text').show();
}else{
$('#Sub_Text').hide();
}
});
</script>
Which seems to be able to work for something simpler without using #Html.EditorFor() in a loop.
It looks as if the script does not follow the same automatic naming changes as those that happen to the RadioButtonFor elements. Resulting in things like this:
Radio:
<input id="Questions_0__Answer" name="Questions[0].Answer" type="radio" value="No" />
While the divs and scripts keep referencing only what was directly entered.
How can you dynamically hide the "Sub_Text" div based on the radiobutton when it is nested in this way?
If there is a way to do this without feeding in a script per EditorFor radio group that would be even better, but all solutions are welcome.
Wrap the html generated by the EditorTemplate in a container so that you can use relative selectors
#model ViewModels.QuestionModel
<div class="question"> // container
<div class="col-lg-2">
#Html.RadioButtonFor(Model => Model.Answer, YesNoNAOptions.Yes)
....
</div>
<div class="col-lg-9">
....
<div class="row">#Model.SubText</div>
</div>
</div>
and then the script can be
$(':radio').change(function () {
// get the associated element containing the SubText value
var row = $(this).closest('.question').find('.row');
if ($(this).val() == 1) {
row.show();
} else {
row.hide();
}
});
Side note: If your QuestionModel.cshtml is in the /Views/Shared/EditorTemplates or /Views/yourControllerName/EditorTemplates folder (which it should be) then the code in _Reporting.cshtml should be just
#model ViewModels.Prime
#Html.EditorFor(x => x.Questions)
No loop is required. The EditorFor() accepts IEnumerable<T> and generates the correct html for each item in the collection.

Spring MVC ModelAndView attributes

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.

Categories

Resources