MV3 input validation - IE8 & IE9 behave differently - asp.net-mvc-3

I'm using DataAnnotations to validate my input fields on a MVC3 application. I'm using regular expressions validations.
I get the validation messages on the UI for IE8 & IE9.
But I notice the difference when I hit the Save button even after the client side validation has failed.
IE9 keeps me on the client side.
On IE8 however, the control goes to the controller action, and I have to have a controller side TryValidateModel so that the validation errors out.
Does anyone know why IE8 is doing a server round trip?
Edit:
Adding the code. This goes into the cshtml.
#using (Html.BeginForm("Person", "Account", FormMethod.Post))
{
<span class="resultError" id="resultError">
#Html.ValidationMessageFor(model => model.Name, "Name should not contain special characters")
</span>
<table>
<tr>
<td class="editor-label">Name:
</td>
<td class="editor-field">#Html.EditorFor(model => model.Name)
</td>
</tr>
</table>
<input type="submit" name="btnKey" value="Save" />
}
This is the partial class using DataAnnotation. The Person class is driven by EF. So I have to create a metadata class to do the validation.
[MetadataType(typeof(personMetadata))]
public partial class person: EntityObject
{
public class personMetadata
{
[Required]
[RegularExpression(#"[A-Za-z0-9]+")]
public object Name { get; set; }
}
}
Edit: Adding the javascript files that are referenced.
"~/Scripts/jquery.validate.min.js"
"~/Scripts/jquery.validate.unobtrusive.min.js"

In my case, which is a lot like yours, I found that updating jquery.validate.js was the way to go. There is a reported bug on version 1.8.0 of jquery validation about IE 7, 8 and 9.
After getting the latest version everything started to work.

Related

How to pass ViewModels into Razor Components in .NET Core 3.1

I have a View MyView.cshtml with the following content:
#using MyProject.ViewModels
#model MyProject.ViewModels.MyViewViewModel
<form asp-action="Test" method="Post">
<component type="typeof(MyProject.Views.Home.Test)" render-mode="ServerPrerendered" />
<input type="submit" value="send"/>
</form>
And I have the Razor Component Test.razor with the following content (with Blazor Syntax):
#page "/Test"
<div class="form-group top-buffer #Visible">
<div class="row">
<div class="col-2">
<label asp-for="TestName" class="control-label"></label>
</div>
<div class="col-3">
<input asp-for="TestName" class="form-control" />
<span asp-validation-for="TestName" class="text-danger"></span>
</div>
</div>
</div>
<button #onclick="Show">Show</button>
#code {
public string Visible { get; set; } = "hidden";
protected async Task Show()
{
Visible = "";
}
}
The Class MyViewViewModel would look like this:
namespace MyProject.ViewModels
{
public class MyViewViewModel
{
[Display(Name = "Test Name:")]
public string TestName { get; set; }
}
}
Works all pretty fine so far. However I now want to use this component as part of a Web form which will be sent to the controller after submission. That's why I need to access and change properties of my ViewModel 'MyViewViewModel'. Unfortunately I did not find any answer in the internet on how to do that. I can't use #model MyProject.ViewModels.MyViewViewModel like in the view because this will give me a compilation error. I wonder if I need to use #inject, but if yes, I don't know how...
(parts are used from this example: https://jonhilton.net/use-blazor-in-existing-app/)
When you mix Blazor in a Razor Page, you can do the following:
Render a Razor Component
Interact with a Razor Component
Pass a Razor Component values
Please keep in mind that you are dealing with two different life-cycles. So if you do work inside of a Razor Component, the component will update but not effect the Razor Page it is hosted inside of. So mixing Razor Components and Pages with forms would be difficult.
More specifically to the OP. To pass data from your ViewModel to the component you may use the following method.
#using MyProject.ViewModels
#model MyProject.ViewModels.MyViewViewModel
<form asp-action="Test" method="Post">
<component type="typeof(MyProject.Views.Home.Test)"
render-mode="ServerPrerendered"
param-Name="#Model.TestName"/>
<input type="submit" value="send"/>
</form>
Test.razor
<h3>HelloWorld</h3>
Hello #Name
#code {
[Parameter]
public string Name { get; set; } = "undefined";
}
About life cycles
Basically when you have a button in Blazor, it will trigger an event which causes the component to re-render. You could imagine it like an iframe, or update-panel. When you have a button in a Razor page, it does a HTTP call round trip and reloads the page entirely. There is no event system in place to tell Blazor to invoke an HTTP call round trip to refresh the Razor page's content and vise versa. You can only one-way data-bind from Razor pages to Blazor, think write-only, and only when the page loads.
To hopefully add to the info. With a ASP.Net Core MVC project host Blazor webassembly, I was trying to pass a viewmodel into a razor component using this code in my view cshtml file:
<component Type="typeof(Leave)" render-mode="WebAssembly" model="new { model = (MyViewModel)#Model})"/>
But it would fail to render the razor component if I tried to access data in the viewmodel from the razor component with an Object not set exception. I think it was accessing the data before the view model has been initialized. Maybe if I set a default value this could avoided?
I found by using this instead I was able to get it working.
#(await Html.RenderComponentAsync<Leave>(RenderMode.WebAssembly,new { model = (MyViewModel)#Model}))
Edit
Seems you also need to register the viewModel class in the services in the Blazor WASM project in the Program.cs file.
builder.Services.AddScoped(sp => new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<MyViewModel,MyViewModel>(); // <= add this line
await builder.Build().RunAsync();`
Without that I would get an error saying the property could not be found.
Hopefully this saves someone else some time :-)

Spring MVC: (If is possible) search form on submit event generate a URI instead of URL pattern format

I have a #Controller with the following #RequestMapping annotation
#RequestMapping(value={"somevalue"},
method=RequestMethod.GET,
produces=MediaType.TEXT_HTML_VALUE)
public String findOneById(#PathVariable String id, Model model){
model.addAttribute(findOneById(id));
return "some view";
}
Observe it uses #PathVariable, based on URI, it works around /persons/{id}, for example for:
/persons/100
/persons/200
it retrieves an expected person.
that #RequestMapping method works fine when:
I put the URL/URI in the same Web Browser address bar (.../persons/200)
when I generate a report of items including for each one a link to see the details (.../persons/200).
I am trying to create a search form such as follows:
<body>
<spring:url var="findOne" value="/persons/" >
</spring:url>
<form action="${findOne}" method="get">
<table>
<tr>
<td><label for="id"><spring:message code="persona.id.label"/></label></td>
<td><input name="id" id="id" value=""/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="search"/></td>
</tr>
</table>
</form>
</body>
My problem is, when I press submit the form always generates/creates the URL to do the search in the format /persons?id=100. I know it is normal and is expected.
But just being curious and of course if is possible:
Question:
What would be the new configuration to generate a URI pattern such as /persons/100 instead of /persons?id=100 when I press the submit button?
I want avoid create a new #RequestMapping method working around with a #RequestParam (it for ?id=100). It to complement the #RequesMapping method working with #PathVariable (it for /100) version.
Thanks

unobtrusive validation RANGE does not work

I am doing an MVC 3 application. I have a model with [Range(1, 175, ErrorMessage="Invalid")].
On one of the controllers the view renders perfectly with all markup for validation. On a second Controller with the same setup the Range validation which is done on a dropdownlist, does not appear on the html markup. I have validation and unostrusiveValidation true on config.web. I am using LINQTOSQL and I have done a partial class to add the additional metadata. The field does pick up the [Display(Name="State")], but the Range is not.
<tr>
<td>#Html.LabelFor(x => x.carta.INVprovincia)</td>
<td>#Html.DropDownListFor(x => x.carta.INVprovincia, Model.provinciaItems, new { #class = "ddlsmall" }) <br /> #Html.ValidationMessageFor(x => x.carta.INVprovincia)</td>
</tr>
<tr>
<td>#Html.LabelFor(x => x.carta.INVmunicipio)</td>
<td>#Html.DropDownListFor(x => x.carta.INVmunicipio, Model.municipiosItems, new { #class = "ddlsmall" }) <br /> #Html.ValidationMessageFor(x => x.carta.INVmunicipio)</td>
</tr>
The Html.XXX helpers won't generate HTML5 data-* attributes used by the unobtrusive validation framework if they are not inside a form which seems to be your case. I guess that the form is contained within a parent view. This bug (IMHO) is fixed in ASP.NET MVC 4. A possible workaround is to put the following on the to of your partial view to fake a form and make the helpers believe that they are inside a form:
#{
ViewContext.FormContext = new FormContext();
}

Validation messages not clearing in MVC 3.0 with unobtrusive jquery

I'm attempting to implement a simple client side validation in a web app I'm working on, and the actual validation message is working. However, when I correct the incorrect input and the control loses focus, the validation message doesn't clear and the invalid class remains on the control. Here's the relevant view code
#model Project.CommentViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true);
<div class="Comment">
<div class="CommentInfo">
Post New Comment:
</div>
<div class="CommentText">
<div class="commentEdit ">
#Html.TextAreaFor(x => x.CommentText, new { #class = "NewCommentTextBox" })
#Html.ValidationMessageFor(x => x.CommentText)
</div>
#Html.HiddenFor(x => x.ProjectID)
</div>
</div>
}
And the view model attribute
[StringLength(50)]
public string CommentText { get; set; }
As I mentioned earlier, once the comment gets too long and the control loses focus, the error message comes up as expected. When the error is fixed however, the error message doesn't disappear and the control stays red. My _Layout page has the relevant script files included in the right order, and my config file has the appSetting variables set correctly. Any idea what's wrong or where I should be looking for the problem at? Thanks very much for any advice.
Resolved the problem. A Telerik grid that was present elsewhere on the page seems to have been conflicting with the validation somehow and broke it. Manually registering the jquery validation scripts with the grid resolved the issue.
#(Html.Telerik().ScriptRegistrar().DefaultGroup(group =>
group.Add("jquery.validate.js").Add("jquery.validate.unobtrusive.js")))
I think they may have resolved this particular issue with a newer version of the Telerik library.

Client-side validation not firing for CompareAttribute DataAnnotation

I'm laying out a view that compares two password strings. The two properties in one of my models are pretty straightforward:
[Required]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed")]
[StringLength(20, MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New Password")]
public string NewPassword { get; set; }
[Required]
[DataType(DataType.Password)]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed")]
[StringLength(20, MinimumLength = 6)]
[Display(Name = "Confirm Password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
Here's my view code:
<table class="fieldset center" width="400">
<tbody>
<tr>
<th width="150">
#Html.LabelFor(m => m.NewPassword)
</th>
<td>
#Html.PasswordFor(m => m.NewPassword, new { #class = "itext3" })
<br /><br />#Html.ValidationMessageFor(m => m.NewPassword)
</td>
</tr>
<tr>
<th width="150">
#Html.LabelFor(m => m.ConfirmPassword)
</th>
<td>
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "itext3" })
<br /><br />#Html.ValidationMessageFor(m => m.ConfirmPassword)
</td>
</tr>
</tbody>
</table>
All of the attributes fire their client-side validation messages when tested, except for the CompareAttribute on ConfirmPassword which is not firing until I hit the server. However, in my controller the ModelState.IsValid = false.
I compared what I'm doing to the default MVC application which is working correctly. Any suggestions for troubleshooting and fixing this?
I'm using MVC 3 RTM.
There is a BUG in jquery.validate.unobtrusive.js (and jquery.validate.unobtrusive.min.js, the minified version). The client-side validation emitted as a result of the [Compare] attribute will ONLY work if the compare-to field is the FIRST field on the form. To fix this bug, locate this line in jquery.validate.unobtrusive.js:
element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
which results in this equivalent:
element = $(options.form).find(":input[name=MyExample.Control]")[0];
Unfortunately that's not the right syntax for find(), and causes it to reference the first input control on the form.
Change that line to:
element = $(options.form).find(":input[name='" + fullOtherName + "']")[0];
which results in this equivalent:
element = $(options.form).find(":input[name='MyExample.Control]'")[0];
Which is the correct syntax, and correctly matches the input control with the specified name.
Locate the same code block in jquery.validate.unobtrusive.min.js, which looks like this:
f=a(b.form).find(":input[name="+d+"]")[0];
and change it to:
f=a(b.form).find(":input[name='"+d+"']")[0];
Take a look at your script tags in your _Layout.cshtml. I'm guessing the issue is probably your jQuery references. Did you start the MVC 3 project form scratch or are you using an example project or something like that?
Here is what happend with me; I had similar issues...
I was using some example code that pointed to ajax.microsoft.com in the src attributes
So, for example one of the script tags looked like this: <script src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
I wanted to get a better handle on what js was executing on the client side so I changed it to this: <script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
That is just an example, I think there were a couple more script tags that I changed as well
So, after changing to internally served jQuery files it worked. I went back and looked at my local .validate.js file... and it was version 1.6. That is how i realized the issue was due to the version of jQuery or it's compatibility with one of the other the js libs anyway.
Bottom line is that it looks like 1.7 doesn't fully function with the validate.unobtrusive.js lib that I had... there may be a newer version that does function with 1.7... like I said, I was tinkering with an example project so there are some unknowns there. I suppose it could also be an incompatibility with the MvcValidation.js lib between it and one of the other js libs too?
At any rate, I'd say the simplest way to state your issue is that you are most likely referencing a bad combination of js libs. I'd say the best failsafe way to get a good combination of js libs is to create a new Asp.Net MVC 3 Project in Visual Studio and see what versions it gives you by default/with the project template... that is assuming that you didn't start the project from scratch. If you DID start it from scratch then may be you changed your layout file to have bad js references or if none of that is true then I suppose it could be a problem with the VisualStudio project templates?... realistically I'd say that is doubtful though. All of that said- I'd say the most likely cause [that I'd wager on anyway] is that you just got in trouble like me trying to quickly use some example code =)
I've tested this with ASP.NET MVC 3 RTM and it worked fine for me:
Model:
public class MyViewModel
{
[Required]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed")]
[StringLength(20, MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New Password")]
public string NewPassword { get; set; }
[Required]
[DataType(DataType.Password)]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed")]
[StringLength(20, MinimumLength = 6)]
[Display(Name = "Confirm Password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
#model SomeAppName.Models.MyViewModel
#{
ViewBag.Title = "Home Page";
}
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.NewPassword)
#Html.PasswordFor(m => m.NewPassword)
#Html.ValidationMessageFor(m => m.NewPassword)
<br/>
#Html.LabelFor(m => m.ConfirmPassword)
#Html.PasswordFor(m => m.ConfirmPassword)
#Html.ValidationMessageFor(m => m.ConfirmPassword)
<br/>
<input type="submit" value="OK" />
}
In this configuration client side validation works perfectly fine for all attributes including the [Compare].
I was never able to make the default way to work. Seems like in JQuery it fires only those validation methods that are registered with JQuery.validator.
Initially I was doing something like this...
jQuery.validator.unobtrusive.adapters.add("equaltopropertyvalidation", ["param"], function (rule)
{
alert("attaching custom validation adapter.");
rule.rules["equaltopropertyvalidation"] = rule.params.param;
rule.messages["equaltopropertyvalidation"] = rule.message;
return function (value, element, param)
{
alert("now validating!");
return value == this.currentForm[param].value;
};
});
But my second alert (now validating!) would never fire even though the first one would. I had seen this article but I was hesitating to implement it like this because it requires me to do something extra but I was lazy.
Well after spending lots of time to figure out what's wrong I implemented it in the way this article suggests and it works. Now it is like this and it works.
<script type="text/javascript">
jQuery.validator.addMethod("equaltopropertyvalidation", function (value, element, param)
{
return value == this.currentForm[param].value;
});
jQuery.validator.unobtrusive.adapters.add("equaltopropertyvalidation", ["param"], function (rule)
{
rule.rules["equaltopropertyvalidation"] = rule.params.param;
rule.messages["equaltopropertyvalidation"] = rule.message;
});
</script>
If you like detail then here it is: Look in jquery.validate.js and search for
check: function (element)
This is the function that is fired to check if the field is valid or not by firing all the applicable rules on the field. Now first it creates an array or rules to fire. It does that using the following code.
var rules = $(element).rules();
Then it iterates over the rules collection, creates a rule element for every item in the collection and then tries to find a corresponding method to fire for that rule. Once the method is found it is fired. It does that using the following code.
var result = $.validator.methods[method].call(this, element.value.replace(/\r/g, ""), element, rule.parameters);
Now the important thing is that if your custom method is not added to the $.validator.methods collection using the JQuery.validator.addMethod IT WILL NOT BE FOUND, even if it is sitting there in the JQuery.validator.unobtrusive.adapters collection.
There might be a way to make this work without using the JQuery.validator.addMethod (and if yes then someone please clarify) but this I believe is the reason the second approach works and the first doesn't.
I was using the following versions of Jquery files
JQuery: 1.6/1.7.1
Jquery.Validate: 1.9.0
JQuery.Unobtrusive.Validate: taken from Microsoft CDN on
2/14/2012.

Resources