Spring MVC ModelAndView attributes - javascript

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.

Related

Having problems with the JavaScript/jquery and c# implementation

I am following a 3 year old c# course on udemy and am unable to solve the problem regarding the implementation of javascript/jquery code. I am suppose to connect the registration code (via jquery/javascript) with a subscription, however my textbox does not work regardless of the information I insert in it, it is not displaying any text, and it should be displaying different messages such are - Could not register the code., The code was successfully added.
Here is my code:
RegisterCodeController:
public class RegisterCodeController : Controller
{
public async Task<ActionResult> Register(string code)
{
if(Request.IsAuthenticated)
{
var userId = HttpContext.GetUserId();
var registred = await SubscriptionExtension.RegisterUserSubscriptionCode(userId, code);
if (!registred) throw new ApplicationException();
return PartialView("_RegisterCodePartial");
}
return View();
}
_RegisterCodePartial:
#if(Request.IsAuthenticated)
{
<div class="panel panel-primary register-code-panel">
<div class="panel-heading">
<h4 class="panel-title">Register Code</h4>
</div>
<div class="panel-body input-group">
<input type="text" class="form-control" placeholder="Enter code here..." />
<button class="btn btn-primary input-group-btn" type="button" >Submit
<h3 class="panel-title">Submit</h3>
</button>
</div>
<div class="alert alert-danger hidden">Could not register code</div>
</div>
}
RegisterCode.js
$(function () {
var code = $(".register-code-panel input");
function displayMessage(success, message)
{
var alert_div = $(".register-code-panel .alert");
alert_div.txt(message);
if (success)
alert_div.removeClass('alert-danger').addClass('alert-success');
else
alert_div.removeClass('alert-success').addClass('alert-danger');
alert_div.removeClass('hidden');
}
$(".register-code-panel button").click(function (e) {
$(".register-code-panel .alert").addClass('hidden');
if (code.val().length == 0)
{
displayMessage(false, "Enter a code");
return;
}
$.post('/RegisterCode/Register', { code: code.val() },
function (data)
{
displayMessage(true, "The code was successfully added. /n/r Please reload the page.");
code.val('');
}).fail(function (xlr, status, error) {
displayMessage(false, "Could not register the code.");
});
});
});
You are making a POST request but looking at your controller action method it looks like a GET. Thus change your API call $.post('/RegisterCode/Register', { code: code.val() }, to $.get('/RegisterCode/Register', { code: code.val() }, probably and use a HTTP verb explicitly in your action method
[HttpGet]
public async Task<ActionResult> Register(string code)
{
}
Solved, I had the RegisterCode.css file with the same name as RegisterCode.js file, it seems to me this caused some confusion while retrieving data in the Bundle.config, so I renamed the .js file and now it works!

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>

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

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.

How can I populate partial view with results from my Controller (ASP.NET M V C)

Before you start reading guys please read comments in code carefully.
I need to display unread emails for currently logged in user.
When user logs in to a system there will be notification that he has some unread emails and when user clicks on a email (that notification) there should be displayed real emails from database like this:
As much as I understand how this might work as a javascript absolutly beginner I guess I need to attach on click event which will call javascript method which will make an ajax call to my controller which will return some results (unread messages from database) to me.
Now I will post code of image you saw above:
<li role="presentation" class="dropdown">
<a href="javascript:;" onclick="GetAllUnreadEmails('#LogedUserId') class="dropdown-toggle info-number" data-toggle="dropdown" aria-expanded="false" style="visibility:#visibility;">
<i class="fa fa-envelope-o"></i>
#if (LoggedInUser != null)
{
// HERE I AM SHOWING ICON LETTER AND NUMBER OF UNREAD that you saw on image above
int unredEmails = EmailController.GetNumberOfUnreadEmails(LoggedInUser.Id);
if (unredEmails > 0)
{
<span class="badge bg-orange" id="inboxunredEmails">#unredEmails</span>
}
}
</a>
// HERE I AM SHOWING UNREAD MESSAGES AS DROPDOWN (that HTML that is disaplyed when it's clicked on letter) AND I AM PLANING TO MOVE THIS CODE TO A PARTIAL VIEW SO THIS PART MIGHT LOOK LIKE THIS : `#Html.Partial("_TopNavEmails");`
<ul id="menu1" class="dropdown-menu list-unstyled msg_list" role="menu">
int unredEmails = EmailController.GetNumberOfUnreadEmails(LoggedInUser.Id);
if (unredEmails > 0)
{
for (int i = 0; i < unredEmails; i++)
{
<li>
<a>
<span class="image"><img src="/myresources/images/john.png"></span>
<span>
<span>John Doe</span>
<span class="time">3 mins ago</span>
</span>
<span class="message">
Message number: #i;
</span>
</a>
</li>
}
}
</ul>
As you can see guys on letter click I attached on click event:
onclick="GetAllUnreadEmails('#LogedUserId')
Here is definition of GetAllUnreadEmails javascript method:
<script>
var GetAllUnreadEmails = function (userId) {
if (userId) {
$.ajax({
method: "GET",
url: '#Url.Action("GetAllUnreadEmails", "Email"),
contentType: "application/json; charset=utf-8",
success: function (data) {
PROBALY HERE I SHOULD SEND DATA TO PARTIAL VIEW
},
error: function (response, textStatus, errorThrown) {
alert("error!");
},
failure: function (response) {
alert("failure!");
}
});
}
}
</script>
And here is GetAllUnreadEmails ActionMethod which should retrieve data back to a PartialView from my controller:
public ActionResult GetAllUnreadEmails()
{
//This is global user that I can access to on view also
if (User.Id != null)
{
List<Emails> resultList = EmailController.GetUnreadEmailsByUserId(User.Id);
return PartialView(resultList);
}
return PartialView("Error, not found!");
}
I guess this part I did so far has some sense, but I am stuck here I don't know how could I on success send data to a partial view and use that data to populate my span elements, so instead of "John Doe" I would like to display real sender from database.
Any kind of help would be great for me.
Thanks guys
Cheers
Answer amended as comments..
You need to render the partial view in your a main parent emails view.. Something like...
Main "Emails" View
#model TestMVC.Controllers.EmailModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Emails</title>
</head>
<body>
<div>
<i class="fa fa-envelope-o"></i>
#if (Model.NoOfUnReadEmails > 0)
{
<span class="badge bg-orange" id="inboxunredEmails">#Model.NoOfUnReadEmails</span>
}
<div id="divUnReadEmails">
#Html.Partial("_UnReadEmails")
</div>
</div>
</body>
</html>
It is this partial view that is replaced by the return content of the ajax post.
Then in the Success section of the post, replace the html content of the containing div.
success: function(data) {
$("#divUnReadEmails").html(data);
}
Looking at your code... I think your partial view would contain the below.
#model TestMVC.Controllers.EmailModel
<ul id="menu1" class="dropdown-menu list-unstyled msg_list" role="menu">
#for (int i = 0; i < Model.Emails.Count; i++)
{
<li>
<a>
<span class="image"><img src="/myresources/images/john.png"></span>
<span>
<span>#Model.Emails[i].EmailFor</span>
<span class="time">#Model.Emails[i].NoOfMinsAgoSent mins ago</span>
</span>
<span class="message">
Message number: #i;
</span>
</a>
</li>
}
</ul>
In your controller, return the main Emails View with the initial data you need.
public ActionResult Emails()
{
var emailModel = new EmailModel();
var emailService = new EmailService();
emailModel.NoOfUnReadEmails = emailService.GetNoOfUnreadEmails("User1") //Your count!
emailModel.Username = "User!"; //Logged on user
return View("Emails", emailModel);
}
Then you can return the partial view for the given user.
public PartialViewResult UnReadEmails(EmailModel model)
{
var newEmailModel = new EmailModel();
var emailService = new EmailService();
newEmailModel.Emails = emailService.GetUnReadEmails(model.Username);
return PartialView("_UnReadEmails", newEmailModel);
}
This should be enough to get you going..
If you want to use ajax best way to do it is so:
First you need to change the GetAllUnreadEmails method from controller to:
public JsonResult GetAllUnreadEmails()
{
//This is global user that I can access to on view also
if (User.Id != null)
{
List<Emails> resultList = EmailController.GetUnreadEmailsByUserId(User.Id);
return Json(resultList);
}
return Json(false);
}
and the ajax call to:
//if the user id is global then there is no need to send it via ajax
<script>
var GetAllUnreadEmails = function () {
$.ajax({
method: "GET",
url: '#Url.Action("GetAllUnreadEmails", "Email")',
contentType: "application/json; charset=utf-8",
success: function (data) {
$('#menu1').html(' ') //to clear the contect
$.each(data, function () {
$('#menu1').append("<p>"+this.Name + " " + this.Email+"<p>")
})
},
error: function (result) {
alert("error!");
},
failure: function (response) {
alert("failure!");
}
});
}
</script>

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