I am trying to integrate recaptcha into my spring mvc application. Below is the code.
#RequestMapping(value="home.htm", method=RequestMethod.POST)
public String processEmailLogin(
#ModelAttribute("examLoginForm") ExamLoginDetails examLoginDetails,
BindingResult result,
Model model,
#RequestParam("recaptcha_challenge_field") String challangeField,
#RequestParam("recaptcha_response_field") String responseField,
HttpServletRequest request, SessionStatus sessionStatus) {
ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
reCaptcha.setPrivateKey("6LcWOOsSAAAAAM48WFr4PfE0Y1LdTJHKC_BxILwl");
/*String challenge = request.getParameter("recaptcha_challenge_field");
String response = request.getParameter("recaptcha_response_field");*/
String remoteAddr = request.getRemoteAddr();
logger.info("Remote machine IP address is : "+remoteAddr);
String emailIdForm = request.getParameter("email");
ReCaptchaResponse reCaptchaResponse = reCaptchaService.checkAnswer(remoteAddr, challangeField, responseField);
logger.info("Shown captcha is : "+challangeField);
logger.info("Entered captcha is : "+responseField);
logger.info("The validated recaptcha response is : "+reCaptchaResponse.isValid());
boolean correctAnswer = false;
correctAnswer = challangeField.equalsIgnoreCase(responseField);
logger.info("Correct answer : "+correctAnswer);
logger.info(":::::: LOADED Login Form CONTROLLER ::::::");
if(!reCaptchaResponse.isValid()) {
model.addAttribute("message", "wrong captcha");
logger.info("Incorrect captcha");
return "login/login";
}
else {
logger.info("::::: Checking captcha response to validate :::::");
model.addAttribute("message", "correct captcha");
LoginService loginService = (LoginService) ctx.getBean("loginService");
examLoginDetails = loginService.performLogin(emailIdForm);
logger.info("Email id is ::::::::::: "+examLoginDetails.getEmailId());
return "home/home";
}
}
My JSP form is as follows:
<%# page import="net.tanesha.recaptcha.ReCaptcha" %>
<%# page import="net.tanesha.recaptcha.ReCaptchaFactory" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="./jsp/scripts/jquery-1.9.1.js"></script>
<script type="text/javascript" src="./jsp/scripts/jquery-ui-1.10.3.custom.min.js"></script>
<link rel="stylesheet" type="text/css" href="./jsp/styles/jquery-ui-1.10.3.custom.min.css" />
<link rel="stylesheet" type="text/css" href="./jsp/styles/page-style.css" />
<link rel="stylesheet" type="text/css" href="./jsp/styles/tooltip.css" />
</head>
<body>
<form action="home.htm" method="post" name="examLoginForm">
<fieldset class="ui-widget ui-widget-content ui-corner-all">
<legend class="ui-widget-header ui-corner-all">Online Examination</legend>
<table class="ui-widget ui-helper-clearfix">
<tr>
<td><label for="email">Please enter your Email Id : </label></td>
<td><input class="ui-widget-content ui-corner-all" type="text" id="emailId" name="email" title="Please provide the email id you used to register with us to take the exam." ></td>
</tr>
<tr>
<td></td>
<td>
<script type="text/javascript" src="http://www.google.com/recaptcha/api/challenge?k=PUBLIC_KEY"></script>
<noscript>
<iframe src="http://www.google.com/recaptcha/api/noscript?k=PUBLIC_KEY" height="300" width="500" frameborder="0"></iframe><br>
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input type="hidden" name="recaptcha_response_field" value="manual_challenge">
</noscript>
<%-- <%
ReCaptcha c = ReCaptchaFactory.newReCaptcha("PUBLIC_KEY","PRIVATE_KEY", false);
out.print(c.createRecaptchaHtml(null, null));
%> --%>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td><span id="LoginBtn"><input class="ui-helper-clearfix ui-corner-all ui-button ui-button-text ui-state-default ui-state-focus ui-state-active ui-state-hover" type="submit" id="login" value="Take Exam" /></span></td>
<td></td>
</tr>
</table>
</fieldset>
</form>
<div id="dialog" title="Confirm Your Email ID">
<p><span class="ui-widget ui-widget-content ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>Are you sure that this is the Email Id you have used to register yourself with us?</p>
</div>
</body>
</html>
The bean configuration in the spring application context is :
<bean id="reCaptchaService" class="net.tanesha.recaptcha.ReCaptchaImpl">
<property name="privateKey" value="PRIVATE_KEY" />
</bean>
The output i see on my console is :
13:12:56,875 INFO LoginFormController:58 - Remote machine IP address is : 10.129.75.57
13:12:57,078 INFO LoginFormController:63 - Shown captcha is : 03AHJ_VuuVrDXTjd0tBEkuONNedE6Bk214GxuOj7DT0o927e5HNgS_IKX7Efuc79liUvsH8VbKbZ7aZ8iElpJog6YqjsvThFu3BuULzPGEPHZKiIk_hnix6N_IXC3aDQaEcBDTPgooOuqs_CwriWe8PuxrzfbzDS2QdnhLuiWqIVqlX2KnZT9BZYo
13:12:57,078 INFO LoginFormController:64 - Entered captcha is : 44642526 2154
13:12:57,079 INFO LoginFormController:65 - The validated recaptcha response is : false
13:12:57,079 INFO LoginFormController:70 - Correct answer : false
13:12:57,079 INFO LoginFormController:71 - :::::: LOADED Login Form CONTROLLER ::::::
13:12:57,079 INFO LoginFormController:79 - ::::: Checking captcha response to validate :::::
What i see from the console output is that the recaptcha_challange_field contains the captcha question in some encripted form and the recaptcha_response_field contains the response for the captcha question in plain text form. So, is it the reason that the recaptcha checkAnswer(remoteAddr, challange, response) method is always evaluating to false?.
Please help me as i am stuck with this problem from a long time now.
For me recaptcha-spring-boot-starter was very helpful and reduces your code a lot:
https://github.com/mkopylec/recaptcha-spring-boot-starter-samples
Related
Currently I'm working on two different systems (deploy jBoss) but they both have a same JSP file.
System 1 : Spring 5 (not SpringBoot)
System 2 : Java Servlet
I had a JSP file that required in both system, however in System 2, it can display correctly, but not in System 1.
This is sample code of the JSP :
<%#page import="com.captcha.botdetect.web.servlet.Captcha"%>
<%
// Adding BotDetect Captcha to the page
Captcha captcha = Captcha.load(request, "exampleCaptcha");
captcha.setUserInputID("captchaCode");
String captchaHtml = captcha.getHtml();
out.write(captchaHtml);
%>
<input id="captchaCode" type="text" name="captchaCode" />
System 2 able display the content correctly , while System 1 show the output as below
System 1 Output
System 1 console error
Compiled JSP
<html lang="en">
<head>
</head>
<!-- JS -->
<script src="js/jquery-3.6.1.min.js"></script>
<script src="js/bootstrap.js"></script>
<body style="padding-top: 0px;">
<div>
<link type="text/css" rel="stylesheet" href="/xxx/botdetectcaptcha?get=layout-stylesheet&t=1676386800" />
<div class="BDC_CaptchaDiv " id="xxx_CaptchaDiv" style="width: 280px !important; height: 50px !important; "><!--
--><div class="BDC_CaptchaImageDiv" id="xxx_CaptchaImageDiv" style="width: 250px !important; height: 50px !important;"><!--
--><div class="BDC_CaptchaImageDiv" style="width:250px; height:40px;"><img class="BDC_CaptchaImage" id="webrf_CaptchaImage" src="/xxx/botdetectcaptcha?get=image&c=xxx&t=29c62e67e7cd426a947e50715ca9b172" alt="Retype the CAPTCHA code from the image" style="" /></div><!--
-->What is BotDetect Java CAPTCHA Library?<!--
--></div><!--
--><div class="BDC_CaptchaIconsDiv" id="webrf_CaptchaIconsDiv" style="width: 24px !important;"><!--
--><a class="BDC_ReloadLink" id="webrf_ReloadLink" href="#" title="Change the CAPTCHA code"><img class="BDC_ReloadIcon" id="webrf_ReloadIcon" src="/xxx/botdetectcaptcha?get=reload-icon" alt="Change the CAPTCHA code" /></a><!--
--><a rel="nofollow" class="BDC_SoundLink" id="xxx_SoundLink" href="/xxx/botdetectcaptcha?get=sound&c=xxx&t=29c62e67e7cd426a947e50715ca9b172" title="Speak the CAPTCHA code"><img class="BDC_SoundIcon" id="xxx_SoundIcon" src="/xxx/botdetectcaptcha?get=sound-icon" alt="Speak the CAPTCHA code" /></a><!--
--><div class="BDC_Placeholder" id="webrf_AudioPlaceholder"> </div><!-- --></div>
<script src="/xxx/botdetectcaptcha?get=script-include&c=xxx&t=29c62e67e7cd426a947e50715ca9b172" type="text/javascript"></script>
<input type="hidden" id="BDC_VCID_webrf" name="BDC_VCID_webrf" value="29c62e67e7cd426a947e50715ca9b172" />
<input type="hidden" id="BDC_BackWorkaround_webrf" name="BDC_BackWorkaround_xxx" value="0" />
<input type="hidden" id="BDC_Hs_xxx" name="BDC_Hs_xxx" value="0fa7d8412e64206ef6b3c1612ed99e7086b1b59c" />
<input type="hidden" id="BDC_SP_xxx" name="BDC_SP_xxx" value="1436062766" />
</div>
<input id="captchaCode" type="text" name="captchaCode" />
</div>
</html>
I had try to print the JSP and Servlet version, both system return the same value
Server Version: WildFly Full 19.1.0.Final (WildFly Core 11.1.1.Final) - 2.1.0.Final
Servlet Version: 4.0 JSP Version: 2.3
I also try with different browser but still no hope .
So I wonder is there anything I can check or any master can help on this ?
Hi I'm having some trouble retrieving information from the user using a spring controller.
the controller looks like this:
#Slf4j
#Controller("indexController")
public class IndexController {
List<String> userInput = new ArrayList<>(Arrays.asList(new String[]{"Apple", "Blackberry", "Strawberry"}));
public List<String> getUserInput() {
return userInput;
}
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(
Model model
){
log.info("home path was hit");
model.addAttribute("options", getUserInput());
model.addAttribute("option", new Object());
return "index";
}
#RequestMapping(value = "createOrder", method = RequestMethod.POST)
public String placeUserOrder(
Model model,
#ModelAttribute("option")String usersInput
){
log.info("createOrder path was hit");
log.info(usersInput);
return "redirect:/home";
}
the index.thml within re main/resource/templates folder looks like this:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>home page</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h2>your orders are:</h2>
<form method="post" action="/createOrder" th:object="${order}">
<th:text alignment-baseline="text-before-edge" aria-atomic="true" > select a copybook</th:text>
<select class="from-control" id="dropDownList">
<option value="0">select copybook</option>
<option th:each="option : ${options}" th:value="${option}" th:text="${option}" >
<input type="hidden" name="${_csrf.usersChoiceFromThymeleaf}" value="${_csrf.token}" />
</option>
<input type="submit" name="createOrder" value="place">
</select>
</p>
</p>
</form>
<th:block th:each="order : ${orders}">
<tr>
<td th:text="${order.value}"></td>
</tr>
</th:block>
</body>
</html>
every time after i set a breakpoint within the post function the option value is empty.
what am i doing wrong?
shouldnt i expect a string from selected dropdown menu?
i tried to fetch an int and it remains empty as well
the model contains also only 2 key value pairs "option"->""
and org.springframework.validation.BindingResult.option -> {BeanPropertyBindingResult#7472} "org.springframework.validation.BeanPropertyBindingResult: 0 errors"
1,
2,
Question:When do we change images dynamically ?
Answer: When user asynchronously upload images, such as using ajaxfileupload.js.
when a user upload one image, and want to upload another one.
If he uses Jcrop, the later images are all shown in size of the first image.
even though he uses following jQuery code to change the size of the new image, it`s still in wrong size.
$("#target").attr("width","400");
$("#target").attr("height","250");
Does anyone know how to use Jcrop and show each images in its real size? I mean when the images is dynamically changed.
Thanks a lot!
The souce code is compressed into a zip file, they are all font-end code, so you may open it once it`s unzipped, please open "crop.html"
you may download the zip file (214KB) here:
download
the content of "crop.html" is:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="js/jquery.min.js"></script>
<script src="js/jquery.Jcrop.js"></script>
<script type="text/javascript">
jQuery(function($) {
var jcrop_api;
initJcrop();
$('#coords').on('change', 'input', function(e) {
var x1 = $('#x1').val(),
x2 = $('#x2').val(),
y1 = $('#y1').val(),
y2 = $('#y2').val();
jcrop_api.setSelect([x1, y1, x2, y2]);
});
function initJcrop() {
$('#target').Jcrop({
onChange: showCoords,
onSelect: showCoords,
onRelease: clearCoords
}, function() {
jcrop_api = this;
});
};
$('#changeToWiderPicture').click(function(e) {
jcrop_api.destroy();
$("#target").attr("src", "img/2.jpg");
$("#target").attr("width", "400");
$("#target").attr("height", "250");
$("#normalPicture").attr("src", "img/2.jpg");
$("#normanPicureIntroduction").html("Original picure:400X250");
$('#changeToWiderPicture').hide();
$('#changeBack').show();
initJcrop();
return false;
});
$('#changeBack').click(function(e) {
jcrop_api.destroy();
$("#target").attr("src", "img/1.jpg");
$("#normalPicture").attr("src", "img/1.jpg");
$("#normanPicureIntroduction").html("Original picure:250X400");
$('#changeBack').hide();
$('#changeToWiderPicture').show();
initJcrop();
return false;
});
});
function showCoords(c) {
$('#x1').val(c.x);
$('#y1').val(c.y);
$('#x2').val(c.x2);
$('#y2').val(c.y2);
$('#w').val(c.w);
$('#h').val(c.h);
};
function clearCoords() {
$('#coords input').val('');
};
</script>
<link rel="stylesheet" href="css/main.css" type="text/css" />
<link rel="stylesheet" href="css/demos.css" type="text/css" />
<link rel="stylesheet" href="css/jquery.Jcrop.css" type="text/css" />
</head>
<body>
<table>
<tr>
<td>
<img src="img/1.jpg" id="target" />
<br>Picture with Jcrop attached
</td>
<td width=40%></td>
<td>
<img src="img/1.jpg" id="normalPicture" />
<br>
<span id='normanPicureIntroduction'>Original picure:250X400</span>
</td>
</tr>
</table>
<form id="coords" class="coords">
<div class="inline-labels">
<label>X1
<input type="text" size="4" id="x1" name="x1" />
</label>
<label>Y1
<input type="text" size="4" id="y1" name="y1" />
</label>
<label>X2
<input type="text" size="4" id="x2" name="x2" />
</label>
<label>Y2
<input type="text" size="4" id="y2" name="y2" />
</label>
<label>W
<input type="text" size="4" id="w" name="w" />
</label>
<label>H
<input type="text" size="4" id="h" name="h" />
</label>
</div>
</form>
<div style="padding-top:20px;">
<button id="changeToWiderPicture" class="btn btn-mini">changeToWiderPicture</button>
<button id="changeBack" class="btn btn-mini" style="display:none;">changeBack</button>
</div>
</body>
</html>
I find a solution from another answer on another question:
use -
$('#image').removeAttr('style');
Or simply overwrite the corresponding CSS style, for example -
$("#target").css({"width":"400px" ,"height":"250px"});
please be aware:
CSS style has a higher priority than the attribute, so sometimes, you find your change does not apply, for example -
$("#target").attr("width","400");
$("#target").attr("height","250");
So, for safer, user CSS style instead of an attribute.
I have three fields department_Id,department_Name,department_location in departmentForm act as a model object in this model form.
I have use annotation to validate the fields. Now, I want to only use two fields in different jsp page say create.jsp and one field in different jsp page say getDepartmentById.
When I press submit button of create.jsp, validation is happening but after providing correct information its not submitted cause in this page.
I haven't give one field department_Id which is auto generated by my DAO layer. So, please help me, how to reject this value to execute my create.jsp page for successfully creating department in database.
When I printed the BindingResult object, it shown as follow:
Field error in object 'departmentForm' on field 'departmentId': rejected value [null];
codes [NotEmpty.departmentForm.departmentId,NotEmpty.departmentId,NotEmpty.java.lang.String,NotEmpty];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [departmentForm.departmentId,departmentId]; arguments [];
default message [departmentId],org.hibernate.validator.constraints.NotEmpty.message},
[Ljava.lang.Class;#4fc4a198,[Ljava.lang.Class;#764d2b11];
default message [may not be empty]`
This is how I coded in controller:
#RequestMapping(value = "/createDepartment", method = RequestMethod.POST)
public String createEmployee(#Valid DepartmentForm departmentForm,
BindingResult bindingResult, Map<String, DepartmentForm> model)
throws Exception {
if (bindingResult.hasErrors()) {
System.out.println(bindingResult);
bindingResult.reject(departmentForm.getDepartmentId());
return "departmentForm";
}
System.out.println("mr ankur jadiy");
model.put("departmentForm", departmentForm);
departmentForm.setUpdateStatus('A');
if (departmentForm.getUpdateStatus() == 'A') {
departmentServiceImpl
.actionDecider(convertDeptFormToDeptBO(departmentForm));
}
return "Success";
}
my DepartmentForm code is as follow:
package com.nousinfo.tutorial.model;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
public class DepartmentForm {
#NotEmpty
#Size(min = 1, max = 20,message="")
private String departmentId;
#NotEmpty
private String departmentName;
private String departmentLocation;
private Character updateStatus;
public String getDepartmentId() {
return departmentId;
}
public void setDepartmentId(String departmentId) {
this.departmentId = departmentId;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
public String getDepartmentLocation() {
return departmentLocation;
}
public void setDepartmentLocation(String departmentLocation) {
this.departmentLocation = departmentLocation;
}
public Character getUpdateStatus() {
return updateStatus;
}
public void setUpdateStatus(Character updateStatus) {
this.updateStatus = updateStatus;
}
}
and my create.jsp is
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%# taglib uri="http://jakarta.apache.org/taglibs/input-1.0" prefix="input"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib uri="http://www.springframework.org/tags" prefix="s"%>
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Create Department</title>
<link rel="stylesheet" href="css/style.css" type="text/css"></link>
</head>
<body>
<table width="1254" height="74" border="0" align="center">
<tr>
<td width="300" height="68" align="center" bgcolor="#99CCFF"><h2>
<span class="style1">Employee Details </span>
</h2></td>
<td width="100" height="68" align="center" bgcolor="#FFFFFF"><img
src="./image/emps.jpg" width="190" height="92" /></td>
</tr>
</table>
<p>
<br />
</p>
<hr size="1" width="786">
<form:form id="form" method="post" action="/EmployeeWebSpring/departmentController/createDepartment"
modelAttribute="departmentForm">
<table>
<tr>
<form:hidden path="updateStatus" />
</tr>
<tr>
<td>
Department_Name:
<font color="red"><form:errors path="departmentName" /></font>
</td>
</tr>
<tr>
<td><form:input path="departmentName" /></td>
</tr>
<tr>
<td>
Department_Location:
<font color="red"><form:errors path="departmentLocation" /></font>
</td>
</tr>
<tr>
<td><form:input path="departmentLocation" /></td>
</tr>
</table>
<br>
<br />
<p> </p>
<br>
<tr>
<td><input type="submit" name="method" value="save" /></td>
<td><input type="submit" name="method" value="cancel" /></td>
</tr>
<hr size="1" width="786">
<p> </p>
</form:form>
</body>
</html>
What the error says is that you're missing value for departmentId, which is not surprising since you defined it as
#NotEmpty
#Size(min = 1, max = 20,message="")
You don't really need to validate departmentId if it's autogenerated by your code. You probably should remove it from the DepartmentForm, especially since it's not in the form, or at least make it optional.
You can make it mandatory in your business object, but the form backing object should reflect what's in the form.
update
If departmentId is a database-generated id, you should set it as disallowed in your controller's InitBinder:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields(new String[] { "departmentId" });
}
I am having a similar as the user here:
ASP.NET MVC 3.0 WebGrid - Ajax Enabled
and here:
http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/22be0501-1b0b-496a-9d0c-f9f5138996ac
However none of the suggestions from those threads is helping here.
The specifics: When running in my local debugger on my Windows 7/IIS 8 machine, I am getting the 'A jQuery script reference is required in order to enable Ajax support in the "WebGrid" helper' message after an ajax postback trying to update. The same code however does not result in a problem in FF or when using IE against a version of the same code running on a server (IIS 6). To make it more confusing, when I first wrote this code, I never had this problem - it has only recently started throwing error.
When the error occurs, the 'correct' updated content flashes on the screen before the whole page redirects to just the partial view and throws the popup on screen. It's like it works correctly but something then resets it and causes it to stop using ajax.
To try to troubleshoot I replaced the .min versions of jquery with the full versions and step through the code. In the debugger the jQuery behaves differently and triggers an "ajaxStop" because of the value in the global ajax counter. When I run the external script debugger against the server version of the same code in the same IE browser, this does not appear to occur (though I still have the 'min' version on the server so can't see exactly). It is this ajaxStop which appears to be forcing the webgrid to think that jQuery isn't loaded on the page and doesn't let the ajax behavior 'stick' - forcing a new page load of just the partial view.
My page is not ultra simple: the main View has several divs containing partial views - divSearch contains the search partial view which is the one calling the ajax postback, and divList contains the grid that needs to be updated based on the search results. The controller method called by the search partial view renders the 'new' contents for the search results into a string and sends them back as part of a JsonResult, which is then assigned to the divList's html via jQuery. (I use this technique in several places in the app, and I've verified that the string being sent back is the correct content for the partial view).
The jQuery (and all the other js includes) are at the top of the layout page and render before any of the partial views.
Main View:
#model WebUI.Areas.Admin.Models.Client.ManageClientsModel
#{
ViewBag.Title = "ManageClients";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<style type="text/css">
.inv
{
visibility: hidden;
}
.vis
{
visibility: visible;
}
</style>
<h2>ManageClients</h2>
<div id="divSearch">
#Html.Partial("_ClientSearch", Model.ClientList)
</div>
<div id="divList">
#Html.Partial("_ClientList", Model.ClientList)
</div>
<br/>
<div id="newSection">
<div id="divEdit"> <button class="toggleNew">
Add Client
</button>
</div>
div class="inv" id="divNew"> #Html.Partial("_Create", Model.ClientVM ) </div>
</div>
<script type="text/javascript">
$(function () {
$(".toggleNew").click(function () {
$("#divNew").toggleClass("inv");
return false;
});
});
</script>
Client Search partial:
#using Core.Entities
#model WebUI.Areas.Admin.Models.Client.ClientListViewModel
<div id="clientSearch">
<form action="#Url.Action("JsonSearch","Client", new { area = "Admin"})"
onsubmit="return formSearchSubmit(this);" >
<div class="divReturnErrors"></div>
#if(TempData["SearchName"] == null)
{
TempData["SearchName"] = Model.SearchName;
}
<br/>
#Html.LabelFor(model => model.SearchName, "Search for client name:")
#Html.EditorFor(model => model.SearchName)
<input type="submit" value="Search" name="btnSearch" />
</form>
</div>
<script type="text/javascript">
function formSearchSubmit(formToSubmit) {
//this line submits the form data to the JsonSearch method
$.post($(formToSubmit).attr("action"),
{ SearchName: $(formToSubmit).find("#SearchName").val() },
function (data) { //this is the callback from JsonSearch
// that handles the results.
if (data["IsSuccess"] == undefined || data["IsSuccess"] == false)
$('div.divReturnErrors').html(data["Errors"]);
//put errors in divReturnErrors
else { //if it succeeds we stuff the string containing the
//partial view into the _ClientList control's div to
//replace previous grid.
$("#divList").html(data["PartialViewHtml"]);
}
}, "json");
return false;
}
</script>
Client List partial view
#model WebUI.Areas.Admin.Models.Client.ClientListViewModel
<h2>Clients</h2>
#helper TrueFalseToYesNo(bool val) {
if (val)
{
<span>Yes</span>
}
else
{
<span>No</span>
}
}
<div id="clientListing">
#{
if(Model.SearchName != null)
{
TempData["SearchName"] = Model.SearchName;
}
var grid = new WebGrid<NeedleFinder.Core.Entities.Client>(null, rowsPerPage: 10,
canSort: false, defaultSort: "ClientName",
ajaxUpdateContainerId:"clientListing");
grid.Bind(Model.Clients, rowCount: Model.TotalRows, autoSortAndPage: false);
#grid.GetHtml(htmlAttributes: new {id= "clientListing"},
tableStyle: "table90",
alternatingRowStyle: "duncanTableAltRows",
headerStyle: "duncanTableHeaders",
columns: grid.Columns(
grid.Column(columnName: "Client ID", style: "logcolumn",
format: item =>
#Ajax.ActionLink(((object)item.ClientID).ToString(),
Model.LinkAction, new { id = item.ClientID }, new AjaxOptions {
HttpMethod = "GET", UpdateTargetId = "divEdit", InsertionMode =
InsertionMode.Replace })),
grid.Column(columnName: "ClientName", style: "logcolumn"),
grid.Column(columnName: "Abbreviation", style: "logcolumn"),
//grid.Column(columnName: "IsActive", style: "logcolumn")
grid.Column(columnName: "IsActive", style: "logcolumn",
format: item => #TrueFalseToYesNo(item.IsActive))
)
)
}
</div>
The 'error' is occurring in this line:
$("#divList").html(data["PartialViewHtml"]);
from the Client Search partial view ajax callback function. This line jumps into the jQuery and while in there is calling ajaxStop.
Please note I have already incorporated the suggestions from the other thread (jQuery includes at the top, putting the webgrid in a div with the same name as the ajaxUpdateContainer target, adding an id property to the webgrid with the same name as well. The suggestion to load the partial explicitly in document.ready does not seem applicable to me since I am trying to populate this div with a string based on inputs rather than just plain load the default 'get' version of the partial view.
Here is the rendered html for the page when it first loads (it doesn't update when you do the ajax post anyway): (Apologies for the wonky formatting but adding 4 indents to each line didn't really work cleanly)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ManageClients</title>
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
<link href="/Content/themes/redmond/jquery-ui.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-1.5.1.js" type="text/javascript"></script>
<script src="/Scripts/jquery-ui-1.8.18.js" type="text/javascript"></script>
<script src="/Scripts/jquery.unobtrusive-ajax.js" type="text/javascript"></script>
<script src="/Scripts/modernizr-1.7.js" type="text/javascript"></script>
<script src="/Scripts/jQuery.Validate.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js" type="text/javascript"></script>
</head>
<script type="text/javascript">
$(document).ready(function () {
$('.dateEditor').datepicker({ dateFormat: "mm/dd/yy" });
});
//This function should be called in your ajax post return are staying on same page
after success and/or you do have server side errors that couldn't have been found
//if there were still client side errors, otherwise you will still have the previous
validation errors.
function ClearValidationErrors() {
var container = $('form').find('[data-valmsg-summary="true"]');
var list = container.find('ul');
if (list && list.length) {
list.empty();
container.addClass('validation-summary-valid').removeClass('validation-summary-
errors');
}
}
</script>
<body>
<style type="text/css">
#topbar
{
background-color: Black;
margin: 0px;
padding: 5px;
}
#adminmenu_nav a
{
float: left;
width: 150px;
}
.logcolumn
{
padding-left: 10px;
padding-right: 10px;
border: 1px solid #BBBBBB;
}
</style>
<script>
$(function () {
$("#adminmenu_nav a").button();
$("#optionsPanel input").button();
$(".asButton input").button();
});
</script>
<div id="topbar">
<img style="margin-left:20px" src="/Content/Images/Logos/NFWhite.png"
alt="NeedleFinder" />
</div>
<div>
<div id="adminmenu_nav" style="float: left">
<table>
<tr><td>Clients</td></tr>
<tr><td>Cases</td></tr>
<tr><td>Users</td></tr>
<tr><td>Roles</td></tr>
<tr><td>Logs</td></tr>
</table>
</div>
<div style="overflow: hidden">
<span class="validation-summary-errors"></span>
<style type="text/css">
.inv
{
visibility: hidden;
}
.vis
{
visibility: visible;
}
</style>
<h2>ManageClients</h2>
<div id="divSearch">
<div id="clientSearch">
<form action="/Admin/Client/JsonSearch" onsubmit="return formSearchSubmit(this);" >
<div class="divReturnErrors"></div>
<br/>
<label for="SearchName">Search for client name:</label>
<input class="text-box single-line" id="SearchName" name="SearchName"
type="text" value="" />
<input type="submit" value="Search" name="btnSearch" />
</form>
</div>
<script type="text/javascript">
function formSearchSubmit(formToSubmit) {
//this line submits the form data to the JsonSearch method
$.post($(formToSubmit).attr("action"),
{ SearchName: $(formToSubmit).find("#SearchName").val() },
function (data) {
if (data["IsSuccess"] == undefined || data["IsSuccess"] == false)
$('div.divReturnErrors').html(data["Errors"]);
else
{
$("#divList").html(data["PartialViewHtml"]);
}
}, "json");
return false;
}
</script>
</div>
<div id="divList">
<h2>Clients</h2>
<div id="clientListing">
<script type="text/javascript">if (typeof(jQuery)=='undefined') alert("A jQuery script
reference is required in order to enable Ajax support in the \"WebGrid\" helper.");
</script><table class="table90" id="clientListing"><thead><tr
class="duncanTableHeaders"><th scope="col">Client ID</th><th
scope="col">ClientName</th><th scope="col">Abbreviation</th><th
scope="col">IsActive</th></tr></thead><tfoot><tr><td colspan="4">1 <a href="#"
onclick="$('#clientListing').load('/Admin/Client/ManageClients?page=2&
amp;__=634750282355486955 #clientListing');">2</a> <a href="#"
onclick="$('#clientListing').load('/Admin/Client/ManageClients?page=3&
amp;__=634750282355506956 #clientListing');">3</a> <a href="#"
onclick="$('#clientListing').load('/Admin/Client/ManageClients?page=4&
amp;__=634750282355516957 #clientListing');">4</a> <a href="#"
onclick="$('#clientListing').load('/Admin/Client/ManageClients?page=5&
amp;__=634750282355536958 #clientListing');">5</a> <a href="#"
onclick="$('#clientListing').load('/Admin/Client/ManageClients?page=2&
amp;__=634750282355546959 #clientListing');">></a> </td></tr></tfoot><tbody>
<tr><td class="logcolumn"><a data-ajax="true" data-ajax-method="GET" data-ajax-
mode="replace" data-ajax-update="#divEdit" href="/Admin/Client/_Edit/2042">2042</a>
</td><td class="logcolumn">ABC Legal</td><td class="logcolumn">ABC123</td><td
class="logcolumn"> <span>Yes</span>
</td></tr><tr class="duncanTableAltRows"><td class="logcolumn"><a data-ajax="true"
data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#divEdit"
href="/Admin/Client/_Edit/2044">2044</a></td><td class="logcolumn">ABC Legal
3</td><td class="logcolumn">ABC125</td><td class="logcolumn">
<span>Yes</span>
</td></tr><tr><td class="logcolumn"><a data-ajax="true" data-ajax-method="GET"
data-ajax-mode="replace" data-ajax-update="#divEdit" href="/Admin/Client/_Edit
/2045">2045</a></td><td class="logcolumn">ABC Legal 4b</td><td
class="logcolumn">ABC4bb</td><td class="logcolumn"> <span>Yes</span>
</td></tr><tr class="duncanTableAltRows"><td class="logcolumn"><a data-ajax="true"
data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#divEdit"
href="/Admin/Client/_Edit/2047">2047</a></td><td class="logcolumn">ABC Legal
6</td><td class="logcolumn">ABC6</td><td class="logcolumn"> <span>No</span>
</td></tr><tr><td class="logcolumn"><a data-ajax="true" data-ajax-method="GET"
data-ajax-mode="replace" data-ajax-update="#divEdit" href="/Admin/Client/_Edit
/2048">2048</a></td><td class="logcolumn">ABC Legal 7</td><td
class="logcolumn">ABC7</td><td class="logcolumn"> <span>Yes</span>
</td></tr><tr class="duncanTableAltRows"><td class="logcolumn"><a data-ajax="true"
data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#divEdit"
href="/Admin/Client/_Edit/2049">2049</a></td><td class="logcolumn">ABC Legal
8a</td><td class="logcolumn">ABC8a</td><td class="logcolumn">
<span>Yes</span>
</td></tr><tr><td class="logcolumn"><a data-ajax="true" data-ajax-method="GET"
data-ajax-mode="replace" data-ajax-update="#divEdit" href="/Admin/Client/_Edit
/2050">2050</a></td><td class="logcolumn">ABC Legal 9</td><td
class="logcolumn">ABC9</td><td class="logcolumn"> <span>Yes</span>
</td></tr><tr class="duncanTableAltRows"><td class="logcolumn"><a data-ajax="true"
data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#divEdit"
href="/Admin/Client/_Edit/2043">2043</a></td><td class="logcolumn">ABC
Legal2</td><td class="logcolumn">ABC124</td><td class="logcolumn">
<span>Yes</span>
</td></tr><tr><td class="logcolumn"><a data-ajax="true" data-ajax-method="GET"
data-ajax-mode="replace" data-ajax-update="#divEdit" href="/Admin/Client/_Edit
/2046">2046</a></td><td class="logcolumn">ABC Legal5</td><td
class="logcolumn">ABC5</td><td class="logcolumn"> <span>Yes</span>
</td></tr><tr class="duncanTableAltRows"><td class="logcolumn"><a data-ajax="true"
data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#divEdit"
href="/Admin/Client/_Edit/2051">2051</a></td><td class="logcolumn">ACE Legal
1</td><td class="logcolumn">ACE1</td><td class="logcolumn">
<span>Yes</span>
</td></tr></tbody></table></div>
</div>
<br/>
<div id="newSection">
<div id="divEdit"> <button class="toggleNew">
Add Client
</button></div><!-- placeholder for _Edit., will replace button when editing//-->
<div class="inv" id="divNew">
<form action="/Admin/Client/JsonCreate" onsubmit="return formCreateSubmit(this);">
<div>
<div class="titleText">Add Client</div>
<div class="divClientSideVal">
<div class="validation-summary-valid" data-valmsg-summary="true"><ul><li
style="display:none"></li>
</ul></div>
</div>
<div class="divCreateErrors"></div>
<fieldset>
<table>
<tr>
<td>
<label for="CurrentClient_ClientName">Name</label>
</td>
<td><input data-val="true" data-val-length="Client Name must be 50
characters or fewer" data-val-length-max="50" data-val-
required="Client Name is required."
id="CurrentClient_ClientName" name="CurrentClient.ClientName"
style="width: 120px;" type="text" value="" />
</td>
<td><span class="field-validation-valid" data-valmsg-
for="CurrentClient.ClientName" data-valmsg-replace="false">*</span></td>
</tr>
<tr>
<td>
<label for="CurrentClient_Abbreviation">Abbreviation</label>
</td>
<td><input data-val="true" data-val-length="Abbreviation must be 6
characters or fewer" data-val-length-max="6" data-val-
required="Abbreviation is required." id="CurrentClient_Abbreviation"
name="CurrentClient.Abbreviation" style="width: 120px;" type="text"
value="" />
</td>
<td><span class="field-validation-valid" data-valmsg-
for="CurrentClient.ClientName" data-valmsg-replace="false">*</span></td>
</tr>
<tr>
<td>
<label for="CurrentClient_IsActive">Is Active:</label>
</td>
<td>
<input data-val="true" data-val-required="The IsActive field is
required." id="CurrentClient_IsActive" name="CurrentClient.IsActive"
type="checkbox" value="true" /><input name="CurrentClient.IsActive"
type="hidden" value="false" />
</td>
</tr>
<tr>
<td>Enable for installations:</td>
<td>
<select Multiple="multiple" id="SelectedInstallations"
multiple="multiple" name="SelectedInstallations" style="width:
120px;">
<option value="1">Dev</option>
<option value="2">FakeDev</option>
</select>
</td>
</tr>
</table></fieldset>
<p>
<input type="submit" value="Create" name="btnCreate" />
</p>
</div>
</form>
<script type="text/javascript">
function formCreateSubmit(formToSubmit) {
var installations = "";
$('#SelectedInstallations option').each(function (i) {
if (this.selected == true) {
if (installations.length > 0) {
installations += ",";
}
installations += this.value;
}
});
//this line submits the form data to the JsonCreate method
$.post($(formToSubmit).attr("action"),
{ ClientName: $(formToSubmit).find("#CurrentClient_ClientName").val(),
Abbreviation:
$(formToSubmit).find("#CurrentClient_Abbreviation").val(),
IsActive:
$(formToSubmit).find("#CurrentClient_IsActive").is(':checked'),
SelectedInstallations: installations,
Action: "Create"
},
function (data) {
if (data["IsSuccess"] == undefined || data["IsSuccess"] == false)
{
if (data["Errors"] != undefined &&
data["Errors"].length > 0)
{
ClearValidationErrors();
('div.divCreateErrors').html(data["Errors"]);
} else {
$('div.divCreateErrors').html("");
}
} else {
window.location = "/Admin/Client/ManageClients";
}
}, "json");
return false;
}
</script> </div><!-- placeholder for _Create //-->
</div>
<script type="text/javascript">
$(function () {
$(".toggleNew").click(function () {
$("#divNew").toggleClass("inv");
return false;
});
});
</script>
</div>
</div>
<div class="InternalMenu">Logging - <a href="/Admin
/Client/ManageClients">Client</a></div>
</body>
</html>
Any suggestions would be greatly appreciated as I'm pretty stumped as to what is causing the problem.
Ok - I figured out the problem, so figure I will report back.
The issue was actually more MVC than jQuery related but it was a problem with how I had put my search form together.
I had my search button set with type=submit and also had an onsubmit eventhandler doing the ajax postback. What was occurring was that the ajax postback was doing exactly what it was supposed to but when it completed, the submit was triggering a "get" and the "get" handler in the controller for the search form returns just a PartialViewResult --> so this was replacing the screen contents as a normal load. The error was being thrown because the partial view did not have the layout with the includes.
So the fix was to
a) change the button type to "button"
b) remove the "onsubmit" from the form declaration
c) add an id="frmSearch" to the form (for easy selectivity)
d) modify the formSearchSubmit to take the action from $("#frmSearch").action instead of from the function argument (which is a button, not the form).
After that, it submitted the ajax post correctly and handled the return update w/o triggering another submit of the form.