I have setup a simple example to show a form inside a jquery UI dialog and wish to enable inline client side validation on that form
I have then added the scripts to my master page
<script type="text/javascript" src="<%: Url.Content( "~/_assets/js/jquery-1.4.3.min.js" )%>"></script>
<script type="text/javascript" src="<%: Url.Content( "~/_assets/js/jquery.validate.min.js" )%>"></script>
<script type="text/javascript" src="<%: Url.Content( "~/_assets/js/MicrosoftMvcJQueryValidation.js" ) %>"></script>
and then I have enabled Client Side Validation through the following code
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm() { %>
<% } %>
Then, I dont know how to enable inline validation for every input so when the user leaves the focus from any of them validation occurs.
The client side validation seems to work only after I have done a submit. But that is not a "client side validation" as the attributes get validated from my server code...
Any suggestion?
Finally I have got through the solution.
First of all, my forms were never binded to validation callbacks provided by the code inside the MicrosoftMvcJQueryValidation.js script. This because I am using jQuery dialogs and the form is inside the dialog while the script included in the master page.
My first attempt toward the solution has been to modify the MicrosoftMvcJQueryValidation.js. In particular I have added a function EnableClientSideValidation() where I moved the code that was in the $(document).ready function as in the following code sample
function EnableClientSideValidation() {
var allFormOptions = window.mvcClientValidationMetadata;
if (allFormOptions) {
while (allFormOptions.length > 0) {
var thisFormOptions = allFormOptions.pop();
__MVC_EnableClientValidation(thisFormOptions);
}
}
}
$(document).ready(function () {
EnableClientSideValidation();
});
Then I have called the same function inside a script block that I have placed in the dialog markup code $(document).ready() function
With the help of firebug I have placed a breakpoint inside the EnableClientSideValidation() function and then experienced the fact that was called only when the main page was ready but not from the dialog. This was due to the fact that I had my "dialog" script block inside the <form>...</form> tag and so the script did not worked.
Code like this
<% using (Html.BeginForm()) { %>
//DIALOG FORM CODE WAS HERE
<script type="text/javascript">
$(document).ready(function () {
EnableClientSideValidation();
});
</script>
<% } %>
has been changed to
<% using (Html.BeginForm()) { %>
//DIALOG FORM CODE WAS HERE
<% } %>
<script type="text/javascript">
$(document).ready(function () {
EnableClientSideValidation();
});
</script>
Finally everything started working! I would like to thanks vandalo and kdawg for helping in finding a solution. There was something still missed but your answers have stimulated my head.
I am posting this for other that can have the same problem.
OK, so here's what I did to get MicrosoftMvcJQueryValidation to work for me in an AJAX/PartialView environment. It's relevant, because essentially both instances (my AJAX/PartialView stuff and your onBlur triggering) require explicit control of when the validation methods are called. I'll try my best to capture everything you need to do, because I ended up having to edit my MicrosoftMvcJQueryValidation.js file to get it AJAX-enabled. However, I don't believe any of my edits are required for what you want.
The key lies in being able to access the validation functions that MicrosoftMvcJQuery generates. Fortunately, it adds it to the form element via a property called validationCallbacks.
In my custom submit function, I access and call these callbacks like this (form is the DOM element, not a jQuery object):
// this taps into the mvc clientside validation functionality.
// this is a roundabout way of calling jquery.validate() as
// that is what's going on the in callback() function
validationCallbacks = form.validationCallbacks;
if (validationCallbacks) {
for (i = 0; i < validationCallbacks.length; i += 1) {
callback = validationCallbacks[i];
if (!callback()) {
// subsequent submit handlers should check for
// this value before executing
event.cancelBubble = true;
return false;
}
}
}
I then have my context-specific submit functions check event.cancelBubble before continuing.
For your case, you could have this code be called on the blur event for each input in your form. Granted, it's not the most efficient solution, as each function in the validationCallbacks array validates the entire form, but it will trigger validation on each blur. (validationCallbacks is an array to support multiple forms that require validation.)
Sorry it's not super specific to your situation, but it should get what you need.
I have my earlier answer about how to manually call the validation callbacks created by MicrosoftMvcJQueryValidation.js, however, there may be a simpler answer. (I'm leaving my first answer as future reference for anyone.)
The options for jQuery's Validation plug-in give you the ability to change which event triggers validation. From http://docs.jquery.com/Plugins/Validation/validate#toptions, we have the following option properties: onsubmit, onfocusout, and onkeyup. You should be able assign these options values appropriately and have jQuery Validation behave like you want.
You MAY need to tweak MicrosoftMvcJQueryValidation.js to allow for the setting of options for when it calls validation. I had to do that with my edited copy.
You can follow this example:
There's a problem with the script in MicrosoftMvcJQueryValidation.js which must be updated.
Change the script MicrosoftMvcValidation.js in the step 3.
Model:
Namespace Models
Public Class Customer
Private _Name As String = ""
<DisplayName("Name")> _
<Required(ErrorMessage:="{0}: Mandatory field.")> _
<StringLength(10, ErrorMessage:="{0}: Max lenght 10.")> _
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _Surname As String = ""
<DisplayName("Surname")> _
<Required(ErrorMessage:="{0}: Mandatory field.")> _
<StringLength(10, ErrorMessage:="{0}: Max lenght 10.")> _
Public Property Surname() As String
Get
Return _Surname
End Get
Set(ByVal value As String)
_Surname = value
End Set
End Property
End Class
End Namespace
<%# Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of MvcApplication1.Models.Customer)" %>
<%# Import Namespace="MvcApplication1.jQuery" %>
...
<% Html.EnableClientValidation()%>
<% Using (Html.BeginForm())%>
<fieldset id="FormEditSet">
<div>
<div>
<%=Html.LabelFor(Function(m) m.Name)%>
<%=Html.EditorFor(Function(m) m.Name)%>
<%=Html.ValidationMessageFor(Function(m) m.Name, "*")%>
</div>
<div>
<%=Html.LabelFor(Function(m) m.Surname)%>
<%=Html.EditorFor(Function(m) m.Surname)%>
<%=Html.ValidationMessageFor(Function(m) m.Surname, "*")%>
</div>
</div>
</fieldset>
<input type="image" src="<%=Url.Content("~/Content/Images/page_save_big.png")%>"
value="Save" title="Save" style="border: none;" />
<%End Using%>
Html.ValidationSummaryJQuery is a new extension method you have to define (follow the example).
Remember to put the script at the bottom of the page:
<script src="<%=Url.Content("~/Scripts/MicrosoftAjax/MicrosoftMvcJQueryValidation.min.js")%>" type="text/javascript"></script>
You need to bind your input fields to properties in your controller, then use the Required attribute on your properties - see http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx for an example.
Related
I am trying to do the following:
The object I give to the Viewpage has a list, I do a foreach in the HTML and I create a number of components. Now, I want the IDs of those components to somehow be linked to the object from the List.
(The reason I am trying to do this is because I want to show a button, when they press that button the shown content will change and they should see something else, I will use javascript to achieve this)
Therefor I am trying to make the id of those components dynamic, by for example stating
id="button <%= item.id%>" however this does not seem to work. I have searched alot on google but I haven't found a solution yet which is why I turn to you guys.
I'll link my code as well, I deleted some of the parts that were unnecessary (but added the javascript):
<script type="text/javascript">
function AlterPanel(thePanel) {
var panel = document.getElementById("region"+thePanel);
panel.style.display = 'block';
var button = document.getElementById("button"+thePanel);
button.style.display = 'none';}
</script>
<%foreach (TeamDTO team in Model.List.Teams)
{ %>
<a id="button<%= team.Number %>" onclick="AlterPanel(<% team.Number%>)">
Add member</a>
<div Visible="false" id='region<%= team.Number %>' runat="server">
Please select one:
<%: Html.DropDownListFor(V => V.memberID, new SelectList(Model.members, "ID","Name")) %>
</div>
<% } %>
I eagerly await a reply and thank you in advance.
I guess your Queastion is:
-you have some "button DropDownList" pair, button is visiable, DropDownList is invisible, now if user click the button then DropDownList will showup.
OK, now your View maybe :
<%foreach (TeamDTO team in Model.List.Teams)
{ %>
<a onclick="AlterPanel(<% team.Number%>)">
Add member</a>
<div id="region<%= team.Number %>" style="display:none">
Please select one:
<%: Html.DropDownListFor(V => V.memberID, new SelectList(Model.members, "ID","Name")) %>
</div>
<% } %>
I use JQuery in javascript part like this:
<script type="text/javascript">
function AlterPanel(thePanel) {
$("#region" + thePanel.toString()).css("display", "block");
}
</script>
Don't forget include the following file in View():
<script type="text/javascript" src="<%= Url.Content("~/Scripts/jquery-1.4.1.min.js") %>"></script>
if the answer is not what you want, let mt know and I can help you~ :)
I want to use jQuery ($.post) to submit my html form, but I want to use the client side validation feature of MVC 2. Currently I hook up the post function to the "OnSubmit" event of the form tag, but I can't hook into the validation, ideally I want to be able to do
if (formIsValid) {
$.post('<%=Url.Action("{some action}")%>'...
}
Please note, Client side validation is working with jQuery.validation, I just can't get it to test if the validation was successful or not before I post my data.
Andrew
The final solution
<%
Html.EnableClientValidation();
using (Html.BeginForm("Register", "Account", FormMethod.Post, new { id = "registrationForm" })) {
%>
...
<button type="submit" onclick="return submitRegistration();">Register</button>
<%
}
%>
<script type="text/javascript">
function submitRegistration() {
if ($("#registrationForm").valid()) {
$.post('<%=Url.Action("{some action}")'...
}
// this is required to prevent the form from submitting
return false;
}
</script>
You can initiate jQuery validation on the button click event. Place the following inside your button-click event-handler:
if ($('form').valid())
//take appropriate action for a valid form. e.g:
$('form').post('<%=Url.Action("{some action}")%>')
else
//take appropriate action for an invalid form
See the Validation plugin documentation for more information.
Have: Using ASP.NET MVC 2, DataAnnotationsModel based server validation, and client validation with jQuery. Anything in my model is validated perfectly on the client with jQuery based validation (jQuery.validate and MicrosoftMvcJQueryValidation.js).
Need: Adding an additional HTML <input type="checkbox" id="terms" /> to my form. I need jQuery validation to require that this checkbox is checked AND somehow hook it in with whatever jQuery client script MVC is automagically controlling. Yes, I know it won't validate on the server side, but I don't need or want it to.
Seems like it should be simple but I'm new to MVC, a total beginner at jQuery, and my searches have been coming up blank.
Any help would be appreciated!
Here's a solution. It mimics what mvc does to hook into jQuery validation. So there's a checkbox called Accept that doesn't belong to the model. The script must go after the form and it adds all the validation meta data for that field.
<%
Html.EnableClientValidation(); %>
<% using(Html.BeginForm("Show"))
{ %>
<%= Html.EditorForModel() %>
<div class="editor-field">
<%= Html.CheckBox("Accept", new { #class = "required" })%>
<span class="field-validation-valid" id="Accept_validationMessage"></span>
</div>
<input type="submit" value="Submit" />
<%} %>
<script type="text/javascript">
window.mvcClientValidationMetadata[0].Fields.push({
FieldName: "Accept",
ReplaceValidationMessageContents: true,
ValidationMessageId: "Accept_validationMessage",
ValidationRules: [{ ErrorMessage: "The Accept field is required.", ValidationType: "required", ValidationParameters: {}}]
});
</script>
Might I suggest using a ViewModel for every View (put all of your dataannotations in there). Then you can create a boolean model property for your checkbox and set it to required.
From there, if you're posting the model back to the controller, you can simply use AutoMapper to map the ViewModel to the needed model, or simply map the properties yourself.
Either way, it is good practice to use a ViewModel for every view. Remember a ViewModel's job is to try and contain everything required in the view. This definitely means that it can and will have other data that is not required in the Model.
Try this
$(document).ready(function() {
//// Assuming your form's ID is 'form0'
$("#form0").submit(function() {
if ($("#terms").attr('checked')) {
return true;
}
else
{
//// Error message if any
return false;
}
});
});
If you are using Ajax.BeginForm() with multiple submit buttons similar to this:
// View.aspx
<% using (Ajax.BeginForm("Action", "Controller",
new AjaxOptions { UpdateTargetId = "MyControl", }))
{ %>
<span id="MyControl">
<% Html.RenderPartial("MyControl"); %>
</span>
<% } %>
//MyControl.ascx
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<input name="prev" type="submit" value="prev" />
<input name="next" type="submit" value="next" />
//...
Everything is submitted to the controller fine but the params for the submit button that was clicked are absent from the Request. In otherwords Request["next"] and Request["prev"] are always null.
I looked in to the JavaScript in Microsoft.MvcAjax.js and it looks like the function Sys_Mvc_MvcHelpers$_serializeForm completely skips over the inputs that are of type 'submit'.
This doesn't seem logical at all. How else can you find out what button has been clicked?
It looks like a bug to me. Is there any logical reason to skip these form parameters?
UPDATE: 2009-11-21
I downloaded MVC Release 2 Preview 2 and looked to see if this problem was fixed.
I did a quick test and found similar results to MVC Release 2 Preview 1.
I don't believe it is fixed yet.
UPDATE: 2009-08-07
I downloaded MVC Release 2 Preview 1 and looked to see if this problem was fixed.
I see a new function in the script MicrosoftMvcAjax.debug.js called _serializeSubmitButton and I see that when Ajax.BeginForm() renders the output there is a onclick event but when this event fires it generates an error "Microsoft JScript runtime error: 'Sys.Mvc.AsyncForm' is null or not an object".
In short it looks like a fix was attempted but it isn't working yet or I need to do something more. The bad news is if it isn't the later then Ajax Forms will be broken for everyone until the fix is complete.
UPDATE: 2009-05-07
I received feedback today from Microsoft confirming that this is a bug. They have logged the defect and said they hope to have it fixed in a future release.
For reference I'm leaving the details of my investigation that I submitted to Microsoft. Appologies for the long post but perhaps it will be useful for anyone trying to create a work around..
There are a couple problems in the Ajax support in MVC. To illustrate, consider the pattern illustrated in several examples on the web:
//===========
// View.aspx
//===========
<% using (Ajax.BeginForm("Action", "Controller",
new AjaxOptions { UpdateTargetId = "MyControl", HttpMethod = "POST"}))
{ %>
<span id="MyControl">
<% Html.RenderPartial("MyControl"); %>
</span>
<% } %>
//================
// MyControl.ascx
//================
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<input name="startIndex" type="hidden" value="0" />
<%= Ajax.ActionLink("Prev", "PrevAction",
new AjaxOptions() { UpdateTargetId="MyControl", HttpMethod="POST"}) %>
<%= Ajax.ActionLink("Next", "NextAction",
new AjaxOptions() { UpdateTargetId="MyControl", HttpMethod="POST"}) %>
//...
Expected:
It is just a list that can the user can page forward and back without updating the entire page.
Given this setup. I expect 2 links labeled "Prev" and "Next". Clicking on "Prev" should fire the PrevAction method in the controller as a post and the value in the hidden field named "startIndex" should be available in the request parameters. I expect similar results when clicking the Next link.
Actual:
The reality is that the request object contains NONE of the form parameters even though it shows that it came in as a POST.
In order to get any of the parameters using action link they must be explicitly supplied through the variation of ActionLink that includes parameters. When this is used the parameters become part of the URL of the link which defeats the purpose of having a POST.
So why is the javascript wrong?
I dug into the javascript code that is used to handle the submit for the example I posted with my question and I now better understand why it doesn't handle it. The reason appears to be related to the way they have wired up events and what I believe is a shortcoming in Internet Explorer.
The way it currently works is that the Ajax.BeginForm() helper class generates a form tag with an onsubmit() function to intercept the form submit event. When the user clicks on a submit button the onsubmit() function fires and recieves parameters, one of which is the event.
The MicrosoftMvcAjax scripts look at the event, bundle up the form properties that are supposed to be submitted and sends the request off to the server. The problem is that per WC3 standards only the successful controls are supposed to be posted. In the case of submit buttons this is the button that was actually clicked. Under internet explorer there is no way to determine which button actually caused the submit event to fire so the script just skips all submit buttons.
(In Firefox the event contains a property called "explictOriginalTarget" which points to the button that actually caused the event in the first place)
Whats the fix?
Microsoft should be fixing it. However if we need something sooner I believe the only option is to hack the MicrosoftMvcAjax scripts to wire up events differently. I have found that the form can be wired to a handle a mousedown event where the button clicked can be saved in a global variable where the onsubmit handler can insert it into the post parameters.
Here is some code that I was testing to illustrate this technique. I have confirmed it works in both IE8 and FireFox but I haven't tried to hack it into the MVC Ajax scripts yet... If I get more time. I may post the results here.
<script type="text/javascript">
var _clicked = "";
function onSubmit(e) {
var targ;
if (!e) var e = window.event;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
if (targ.nodeType == 3) //defeat Safari bug
targ = targ.parentNode;
alert("OnSubmit:" + _clicked + " was clicked.");
return false;
}
function Click(e) {
var targ;
if (!e) var e = window.event;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
if (targ.nodeType == 3) //defeat Safari bug
targ = targ.parentNode;
_clicked = targ.name;
return true;
}
<form action="/Home/StandardForm" method="post"
onsubmit="onSubmit(event)" onmousedown="Click(event)">
<input type="submit" name="StdPrev" value="StdPrev" />
<input type="submit" name="StdNext" value="StdNext" />
</form>
In order for your submit buttons to be "successfull" controls as per the specification, they must be defined within the form element:
http://www.w3.org/TR/html401/interact/forms.html#successful-controls
If you can't nest your submit buttons inside your form, you'll probably need to use javascript (or jquery) to submit your form and pass in an additional paramater to indicate which button was clicked.
I suppose this has been fixed in MVC 2 (or it was never broken). Just make sure your HTML markup validates. The following example should show it works.
Vote.aspx:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!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 runat="server">
<title>Vote</title>
</head>
<body>
<%using (Ajax.BeginForm("Vote", "Voting", new AjaxOptions { UpdateTargetId = "message" }))
{ %>
<%= Html.Hidden("itemId", "1")%>
<p>I love ASP.NET MVC!</p>
<input type="submit" name="voteValue" value="+" />
<input type="submit" name="voteValue" value="-" />
<%} %>
<p id="message"><%= TempData["message"] %></p>
<script type="text/javascript" src="<%= Url.Content("~/Scripts/MicrosoftAjax.js")%>"></script>
<script type="text/javascript" src="<%= Url.Content("~/Scripts/MicrosoftMvcAjax.js")%>"></script>
</body>
</html>
VotingController.aspx:
using System.Web.Mvc;
namespace Examples.FormWithMultipleSubmitButtons.Controllers
{
public class VotingController : Controller
{
public ViewResult Vote()
{
return View();
}
[HttpPost]
public ActionResult Vote(int itemId, string voteValue)
{
switch(voteValue)
{
case "+":
TempData["message"] = "You voted up.";
break;
case "-":
TempData["message"] = "You voted down.";
break;
default:
TempData["message"] = "Your vote was not recognized.";
break;
}
if(Request.IsAjaxRequest())
{
return Content(TempData["message"].ToString());
}
else
{
return View();
}
}
}
}
I had the same issue today (Oct 8, 2010) with my form with multiple submit buttons. The HTML didn't validate. I cleaned it up. It's still doesn't validate (but less error than the original) and now the value of clicked button is submitted.
A possible workaround could be to have each button in a seperate form routed to different actions on your controller.
Not ideal but could work.
I did the following:
<input id="btnSubmit" name="btnSubmit" type="hidden" value="" />
<input type="submit" name="btnSubmit" value="Delete" id = "btnDelete" onclick="$('#btnSubmit').attr('value','Delete');"/>
<input type="submit" name="btnSubmit" value="Save New" id = "btnSaveNew" onclick="$('#btnSubmit').attr('value','Save New');"/>
<input type="submit" name="btnSubmit" value="Save" id = "btnSave" onclick="$('#btnSubmit').attr('value','Save');"/>
i.e. defined a hidden input type with id of "btnSubmit" and on each button added the onclick event as onclick="$('#btnSubmit').attr('value','Delete');". this seems to work
as I was able to get the value of the button clicked in the controller:
public ActionResult SaveCreateBlot(string btnSubmit)
{
}
Inside of an asp.net mvc partial view, I have an Ajax form that posts a value and replaces the contents of its parent container with another instance of the form.
Index.aspx view:
<div id="tags">
<% Html.RenderPartial("Tags", Model); %>
</div>
Tags.ascx partial view:
<% using(Ajax.BeginForm("tag", new AjaxOptions { UpdateTargetId = "tags" }))
{ %>
Add tag: <%= Html.TextBox("tagName")%>
<input type="submit" value="Add" />
<% } %>
The controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Tag(string tagName) {
// do stuff
return PartialView("Tags", ...);
}
The problem is when the new instance of the form returns, the posted value is already stored in the input field. As in, whatever I posted as the 'tagName' will stay in the textbox. Firebug shows that the value is hardcoded in the response.
Is there any way to clear the input textbox's value when returning the partial view?
I've tried:
<%= Html.TextBox("tagName", string.Empty)%>
and
<%= Html.TextBox("tagName", string.Empty, new { value = "" })%>`
neither of which do anything.
EDIT:
I realize there are js solutions, which I may end up having to use, but I was wondering if there were any ways of doing it in the backend?
I'm not sure if this solution is "good enough" for you, but couldn't you just empty the form in a JS callback function from your ajax call? If you're using jQuery on your site, the callback function could look something like this:
function emptyFormOnReturn() {
$(':input').val();
}
I am not entirely sure if it will, but in case the above code also removes the text on your submit button, change the selector to ':input[type!=submit]'.
yes you should use jquery to set values on response
if you change your code to use jquery for ajax operations, you can call you settingvalues function on success callback...example:
http://docs.jquery.com/Ajax/jQuery.ajax#options