I have two logical groups of input fields I need to validate separately using JQuery Validation however since I am using ASP.Net WebForms I'm restricted to having just one form tag on the page.
I've implemented validation groups even though I have one form using the following technique from Dave Ward's blog post. This works perfectly.
To bind the validation event to the ASP.Net form looks as follows:
$("#aspnetForm").validate({
onsubmit: false,
ignore: ":hidden",
errorClass: 'dynamic-class'
});
I need to take this further by having a different errorClass value based on whether I am trying to submit (and validate) Form A or Form B. E.g. "Form A" would have "error-class-a" and "Form B" would have "error-class-B". I actually want to do this with other validation settings such as errorPlacement and errorElement but I've tried to keep this explanation simple.
Is there a way I can inject this behaviour without having to hack away at the JQuery Validation plugin source code?
I started by adding validation groups (as per Dave Ward's blog post) so I had two logical groups. After a VERY long look into the JQuery Validate documentation and source code I narrowed the investigation down to a single function: showErrors(). This gets called each time before any error is potentially displayed whether it is on the form submission event or a blur event of one of the elements. By changing the settings accordingly this ensures the correct display settings are always used for the right element.
In the code below one validation group is set to display errors in a UL list summary and the other inline and with a different css class. I've extended the showErrors() function to dynamically switch the error settings based on which validation group the element that has an error is contained in. You could probably take this further and bind the settings to the validation container to avoid the clunky IF statement, but I've used the simple version below as it better illustrates the solution. Finally I call the defaultShowErrors() which as one would expect calls the default function in the validate framework.
$("#aspForm").validate({
onsubmit: false,
// This prevents validation from running on every
// form submission by default.
// Extend the show errors function
showErrors: function (errorMap, errorList) {
// here we get the element linked to the error.
// we then find out which validation group the element in question
// belongs to and set the correct properties
if (errorList[0]) {
var element = errorList[0].element;
// at the time of calling we configure the settings for the validate form
if ($(element).parents('.validationGroup').attr("id") == "signup") {
this.settings.errorClass = "errorSignUp";
this.settings.errorContainer = $("*[id$='uivalidation']");
this.settings.errorLabelContainer = $("*[id$='uivalidation'] ul");
this.settings.errorElement = "li";
} else {
// these are the defaults
this.settings.errorClass = "error";
this.settings.errorContainer = $([]);
this.settings.errorLabelContainer = $([]);
this.settings.errorElement = "label";
}
}
// call the default show errors function after we have hooked up the correct settings
this.defaultShowErrors();
}
});
This does exactly what I was looking for since it means I do not have to make any changes to the validate framework. This is demonstrated in the full working example below where I am using a CDN for JQuery and JQuery.Validate!
Full Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Multiple Form Validation</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.9/jquery.validate.min.js"></script>
<style type="text/css">
*
{
font-family: Verdana;
font-size: 96%;
}
label
{
width: 10em;
float: left;
}
label.errorLogin
{
float: none;
color: blue;
padding-left: .5em;
vertical-align: top;
}
label.error
{
float: none;
color: red;
padding-left: .5em;
vertical-align: top;
}
p
{
clear: both;
}
.submit
{
margin-left: 12em;
}
em
{
font-weight: bold;
padding-right: 1em;
vertical-align: top;
}
</style>
<script type="text/javascript">
$(function () {
$("#aspForm").validate({
onsubmit: false,
// This prevents validation from running on every
// form submission by default.
// Extend the show errors function
showErrors: function (errorMap, errorList) {
// here we get the element linked to the error.
// we then find out which validation group the element in question
// belongs to and set the correct properties
if (errorList[0]) {
var element = errorList[0].element;
// at the time of calling we configure the settings for the validate form
if ($(element).parents('.validationGroup').attr("id") == "signup") {
this.settings.errorClass = "errorSignUp";
this.settings.errorContainer = $("*[id$='uivalidation']");
this.settings.errorLabelContainer = $("*[id$='uivalidation'] ul");
this.settings.errorElement = "li";
} else {
// these are the defaults
this.settings.errorClass = "error";
this.settings.errorContainer = $([]);
this.settings.errorLabelContainer = $([]);
this.settings.errorElement = "label";
}
}
// call the default show errors function after we have hooked up the correct settings
this.defaultShowErrors();
}
});
// Search for controls marked with the causesValidation flag
// that are contained anywhere within elements marked as
// validationGroups, and wire their click event up.
$('.validationGroup .login').click(ValidateAndSubmit);
$('.validationGroup .signup').click(ValidateAndSubmit);
// Select any input[type=text] elements within a validation group
// and attach keydown handlers to all of them.
$('.validationGroup :text').keydown(function (evt) {
// Only execute validation if the key pressed was enter.
if (evt.keyCode == 13) {
ValidateAndSubmit(evt);
}
});
});
function ValidateAndSubmit(evt) {
// Ascend from the button that triggered this click event
// until we find a container element flagged with
// .validationGroup and store a reference to that element.
var $group = $(evt.currentTarget).parents('.validationGroup');
var isValid = true;
// Descending from that .validationGroup element, find any input
// elements within it, iterate over them, and run validation on
// each of them.
$group.find(':input').each(function (i, item) {
if (!$(item).valid())
isValid = false;
});
// If any fields failed validation, prevent the button's click
// event from triggering form submission.
if (!isValid)
evt.preventDefault();
}
</script>
</head>
<body>
<form id="aspForm" runat="server">
<fieldset class="validationGroup" id="login">
<div id="uivalidation">
<ul></ul>
</div>
<legend>Register</legend>
<p>
<asp:Label ID="uiFirstName" runat="server" AssociatedControlID="uxFirstName" Text="First name:"></asp:Label>
<asp:TextBox ID="uxFirstName" runat="server" CssClass="required"></asp:TextBox>
</p>
<p>
<asp:Button ID="uxRegister" runat="server" Text="Register" CssClass="login" />
</p>
</fieldset>
<fieldset class="validationGroup" id="signup">
<legend>Login</legend>
<p>
<asp:Label ID="uiUserName" runat="server" AssociatedControlID="uxUserName" Text="User name:"></asp:Label>
<asp:TextBox ID="uxUserName" runat="server" CssClass="required"></asp:TextBox>
</p>
<p>
<asp:Button ID="uxLogin" runat="server" Text="Login" CssClass="signup" />
</p>
</fieldset>
</form>
</body>
</html>
If this could be further improved please jump in and edit the code.
Related
I'm trying to use a simple jquery-ui modal dialog as a delete confirmation in an ASP.NET C# application. I've done this many times before, but for some reason in this application it is misbehaving. I see the dialog pop up then it immediately disappears before I can click on either "Yes" or "No". Here's the relevant code (Javascript):
<script type="text/javascript" src="/resources/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="/resources/jquery-ui-1.9.1.custom.min.js"></script>
<link rel="stylesheet" type="text/css" href="/resources/ui-lightness/jquery-ui-1.9.1.custom.css" />
<script type="text/javascript">
var remConfirmed = false;
function ConfirmRemoveDialog(obj, title, dialogText) {
if (!remConfirmed) {
//add the dialog div to the page
$('body').append(String.Format("<div id='confirmRemoveDialog' title='{0}'><p>{1}</p></div>", title, dialogText));
//create the dialog
$('#confirmRemoveDialog').dialog({
modal: true,
resizable: false,
draggable: false,
close: function(event, ui) {
$('body').find('#confirmRemoveDialog').remove();
},
buttons:
{
'Yes, remove it': function() {
$(this).dialog('close');
remConfirmed = true;
if (obj) obj.click();
},
'No, keep it': function() {
$(this).dialog('close');
}
}
});
}
return remConfirmed;
}
//String.Format function (since Javascript doesn't have one built-in
String.Format = function() {
var s = arguments[0];
for (var i = 0; i < arguments.length - 1; i++) {
var reg = new RegExp("\\{" + i + "\\}", "gm");
s = s.replace(reg, arguments[i + 1]);
}
return s;
}
</script>
Here's where I'm using the confirmation function (in the OnClientClick of an <asp:Button> control):
<asp:Button ID="btnRemoveProgram" runat="server" Text="Remove" CausesValidation="false" OnClientClick="ConfirmRemoveDialog(this, 'Please confirm removal', 'Are you sure you wish to remove the selected Program? This cannot be undone.');" />
As I said, I've successfully used this same construct (nearly identical code) many times before; I don't know why it isn't working now. Any ideas will be greatly appreciated, I'm truly stumped on this one.
The runat="server" is telling the button that it should post back to perform events at the server. The OnClientClick will be executed before that on the client side, so you will see the dialog and then immediate the page posts, causing the dialog to disappear.
The problem is that your modal dialog box is not modal in the traditional windows sense. The javascript continues on. The simplest test is to add an alert right before your return, you will see it pops up right after the dialog is shown.
To get around this issue, return false always in the OnContentClick and then in your Yes/No button event handlers use the __doPostback javascript method.
You need to return the remConfirmed to the caller which is the button itself. On your button, do this:
OnClientClick="return ConfirmRemoveDialog(/* the rest of the code */);"
I have a application that has jquery draggable div in it.
I have placed four text fields inside the draggable div to get input from user.
Aspx:
<div id="divAdd" runat="server">
<input id="txtCode" placeholder="Location Code" maxlength="20"
type="text" runat="server" />
<input id="txtName" placeholder="Location Code" maxlength="20"
type="text" runat="server" />
...
</div>
<div>
Javascript:
$("#divAdd").draggable({ cursor: 'move', containment: '#divmap',
drag: function () {
fnHandleMove();
}
});
I cant place the cursor in those textfield in IPAD by tapping on it. However Desktop version works fine.
If i comment out that javascript part, i am able to place the cursor and keypad shows.
Is this bug with jquery UI-draggable or i am doing anything wrong?
Apart from JqueryUI.js i use JqueryTouchPunch.js and JSPlumb.js in the application.
Any help will be appreciated.
OK here's a solution if your textfield whatever HTML element is, isn't focusing,scrolling, selecting words, moving text cursor around the text and whatever different scenarios might come then you may override the jquery.ui.touch.punch.js script. I assume that your element isn't the draggable one but probably a child of it as my case was.
Put a class on your html element, for example class="useDefault".
Then go to the script file and find that part:
...
function simulateMouseEvent (event, simulatedType) {
// Ignore multi-touch events
if (event.originalEvent.touches.length > 1) {
return;
}
event.preventDefault();
var touch = event.originalEvent.changedTouches[0],
simulatedEvent = document.createEvent('MouseEvents');
....
As you can probably see event.preventDefault(); assures that jquery.ui.touch.punch.js
overrides the default behaviors of the browser. To prevent that for our particular class node, make the following modifications:
if (event.originalEvent.touches.length > 1) {
return;
}
var touch = event.originalEvent.changedTouches[0],
simulatedEvent = document.createEvent('MouseEvents');
//As you can see here is your class element check
if (touch.target.className === "useDefault") {
event.stopPropagation();
} else {
event.preventDefault();
}
This solution is tested with webkit browsers only and jQuery UI Touch Punch 0.2.2 release.
Hope that quick solution helps, BR
A variation from kidwon idea:
In the same method, same part:
function simulateMouseEvent (event, simulatedType) {
// Ignore multi-touch events
if (event.originalEvent.touches.length > 1) {
return;
}
event.preventDefault();
replace with:
function simulateMouseEvent (event, simulatedType) {
// Ignore multi-touch events
if (event.originalEvent.touches.length > 1) return;
if (simulatedType == 'mouseup') {
var elem = $(event.originalEvent.changedTouches[0].target);
if (elem.is("input") || elem.is("textarea")) elem.focus();
else event.preventDefault();
} else event.preventDefault();
Hope it helps.
I wrote a javscript which create a popup with certain information.
function Showduplicate() {
isDuplicate = true;
var modal = document.getElementById('duplicate');
modal.style.display = '';
modal.style.position = 'fixed';
modal.style.zIndex = '100';
modal.style.left = '30%';
modal.style.top = '40%';
var screen = document.getElementById('modalScreen');
screen.style.display = '';
return false;
}
modal is a div element. duplicate is also a div element which contains certain checkboxes etc. . Now I check a query in codebehind and call this javascript function accordingly. I tried to use Page.RegisterClientScriptBlock Method but was unsuccessful. So can you guys help me out in calling the javascript function in codebehind.
I think for your scenario, you should be using Page.RegisterStartupScript(). See this post for more info on the distinction between RegisterStartupScript() and RegisterClientScriptBlock().
I'm assuming you're using Asp.Net WebForms and Asp.Net Ajax. (The same technique can be applied to other scenarios as well).
Let's say you have a button like:
<tr>
<td><input id="grid_1">Value</input></td>
<td>Add</td>
</tr>
Then you can control the popup with the following JS code:
function OnAddSuccess(result) {
// Update display
}
function OnAddError(error) {
Showduplicate();
}
function addItem(id) {
$get("LoadingLabel").innerHTML = "Loading...";
PageMethods.TryAddItem(id, $get("grid_"+id).innerHTML, OnAddSuccess, OnAddError);
}
You should not forget:
<asp:ScriptManager ID="ScriptManager" runat="server" EnablePageMethods="true" />
i am using asp.net ajax tab container[ which has 2 tab panel]
under each tab panel i have an div tag. now by default.i have my Activetabindex="0"
now i need to enable css property for the div tag using javscript so that there is no post back happening. i doing like this css property for the tab panel 1 is not getting applied
this is my script what i doing. if i do the same thing in code behind for the ta selected index change it works. but thatcause an post back.
now i need t o do it my javscript only
OnClientActiveTabChanged="PanelClick"
<script type="text/javascript" language="javascript">
function PanelClick(Sender, e) {
debugger;
var CurrentTab = $find('<%=Tab1.ClientID%>');
if( Sender._activeTabIndex==0) {
debugger
document.getElementById('<%=mycustomscroll2.ClientID%>').className = '';
document.getElementById('<%=mycustomscroll2.ClientID%>').Enabled = false;
document.getElementById('<%=mycustomscroll.ClientID%>').className = 'flexcroll';
}
if (Sender._activeTabIndex == 1) {
debugger
document.getElementById('<%=mycustomscroll.ClientID%>').className = '';
document.getElementById('<%=mycustomscroll.ClientID%>').Enabled= false ;
document.getElementById('<%=mycustomscroll2.ClientID%>').className = 'flexcroll';
}
}
</script>
so how to i enable my css property for the div using javascript for the tab panel
anyhelp would be great
thank you
Here is a javascript function which will sort of do what you want:
function PanelClick(Sender, e) {
var scroll1 = $get('<%=mycustomscroll.ClientID%>');
var scroll2 = $get('<%=mycustomscroll2.ClientID%>');
if(Sender._activeTabIndex == 0) {
scroll1.setAttribute('class', 'flexcroll');
scroll2.setAttribute('class', '');
} else if(Sender._activeTabIndex == 1) {
scroll1.setAttribute('class', '');
scroll2.setAttribute('class', 'flexcroll');
}
}
There really is no such thing as "enabled" in HTML and JavaScript. HTML has a "disabled" attribute, but it only applies to these elements: button, input, optgroup, option, select and textarea. It is used like so:
<input type="text" name="txtSomething" id="txtSomething" disabled="disabled">
and in JavaScript, similar to setting the class attribute, above:
$get('txtSomething').setAttribute('disabled','disabled'); // disable the input
$get('txtSomething').setAttribute('disabled',''); // enable the input
But this will not work for other elements like <div> and <span> tags.
k... Strager was able to help me a bit. However, it's still not working quite right. Need someone that knows both MVC and jQuery please...
I have a page that has a image that when clicked on will launch a dialog box that gives the ability to upload a file. Once the dialog closes the image is supposed to refresh with what was uploaded/stored in the database...
All works great the first time. However if I try uploading a 2nd image; the 1st image still displays. It also doesn't seem like my controller method is being called the 2nd time... Below is my code...
I've also eliminated that the page is being cached. Again, the controller method is not being called the 2nd time around...
Controller
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetImage(Guid parentId)
{
var la = Helper.Service.GetAttachmentByEntity(parentId, MembershipProvider.SecurityTicket).ToList();
la.OrderByDescending(x => x.CreatedDate);
return File(la[0].Data, la[0].ContentType);
}
View
<script type="text/javascript">
$(function() {
var iphcObject = $('#<%=imagePlaceHolderControl.ClientID %>');
iphcObject.children().find('#imageUploadDialog').bind('onClose', function() {
iphcObject.children().find('#image').attr('src', '<%=Url.Action("GetImage", "Image", new { parentId = Model.ParentId }) %>');
});
});
</script>
<asp:Panel ID="imagePlaceHolderControl" runat="server">
<div style="border: solid 1px; width: <%=Model.Width%>; height: <%=Model.Height%>; text-align: center; vertical-align: middle;">
<a href="#" onclick="$('#imageUploadDialog').dialog('open');" title="Add a picture...">
<img id="image" src="<%=Url.Content("~/content/images/icon_individual_blank.gif") %>" /></a>
<%Html.RenderDialog("imageUploadDialog", "Upload image...", "ImagePlaceHolder_Upload", "Image", Model, 235, 335); %>
</div>
</asp:Panel>
What is #image shouldn't it be find('img')? Or give the IMG an ID of 'image'.
I don't know ASP, but this line:
iphcObject.find('#image').attr('src', '<%=Url.Action("GetNewImage", "Image", new { parentId = Model.ParentId }) %>');
Appears to be parsed at the time the page is initially viewed, not when the new image is uploaded.
To fetch the new image's URL, send that data in the response to the POST request. This should be easy, especially if you use JSON or raw text for the transfer.
For the jQuery side:
$.post('<%=Url.Action("GetImage", "Image", new { parentId = Model.ParentId }) %>', {}, function(data)
{
iphcObject.find('#image').attr('src', data.newImageUrl);
}, 'json');
If anyone knows how to do it on the ASP side, please edit this post or make another!
You can tack on an arbitrary number to force a refresh on the client side:
<img id="img" src="#Url.Action("GetImage")" alt="random" />
<script type="text/javascript">
var src = $("#img").attr("src");
var count = 0;
setInterval(function () {
$("#img").attr("src", src + "?" + count);
count++;
}, 3000);
</script>
Your <img> does not have the id image so .find('#image') isn't returning any elements.
I don't know ASP, but... Have you checked the HTTP headers? It could be that the image URL is being cached by the browser. Try LiveHTTPHeaders for Firefox and see what calls are actually making it to the server. Firebug may also be of some assistance here (it tracks cache hits in the browser vs network calls).