I'm using ASP .NET MVC 3 with Data Annotations and the jQuery validate plugin.
Is there a way to mark that a certain field (or certain data annotation) should only be validated server-side?
I have a phone number field with a masking plugin on it, and the regular expression validator goes crazy on the user's end. The regex is only a fail-safe (in case someone decides to hack the javascript validation), so I don't need it to run on the client side. But I'd still like the other validation to run client side.
I'm not sure if this solution works on MVC3. It surely works on MVC4:
You can simply disable client side validation in the Razor view prior to render the field and re-enable client side validation after the field has been rendered.
Example:
<div class="editor-field">
#{ Html.EnableClientValidation(false); }
#Html.TextBoxFor(m => m.BatchId, new { #class = "k-textbox" })
#{ Html.EnableClientValidation(true); }
</div>
Here we disable client side validation for the BatchId field.
Also I have developed a little helper for this:
public static class YnnovaHtmlHelper
{
public static ClientSideValidationDisabler BeginDisableClientSideValidation(this HtmlHelper html)
{
return new ClientSideValidationDisabler(html);
}
}
public class ClientSideValidationDisabler : IDisposable
{
private HtmlHelper _html;
public ClientSideValidationDisabler(HtmlHelper html)
{
_html = html;
_html.EnableClientValidation(false);
}
public void Dispose()
{
_html.EnableClientValidation(true);
_html = null;
}
}
You will use it as follow:
<div class="editor-field">
#using (Html.BeginDisableClientSideValidation()) {
#Html.TextBoxFor(m => m.BatchId, new { #class = "k-textbox" })
}
</div>
If anyone has better solutions please let me know!
Hope this help.
You can switch off client-side unobtrusive validation for a single field by adding a data-val='false' attribute:
#Html.TextBoxFor(m => m.BatchId, new { data_val = "false" })
This will override the data-val='true' attribute that MVC adds due to any System.ComponentModel.DataAnnotations attributes. The HTML element will still be decorated with other validation attributes (e.g. data-val-required) but they won't have any effect.
(Note the underscore in data_val above. MVC automatically converts underscores to hyphens in anonymous type properties, so data_val becomes data-val when rendering the HTML)
MVC5 use jquery.validate
http://jqueryvalidation.org/rules/
If you want to remove validations in MVC5 client-Side you need to do the following:
Remove all validations on 'myinput'
$("#myinput").rules("remove");
Specific validations
$("#myinput").rules("remove", "min max" );
Listing the validations can help
$("#myinput").rules();
Then you will need to correct your Code Behind to validate manually your model or differently because ModelState.IsValid will be false. Using ModelState.Clear() and TryValidateModel can then be handy.
Edit:
Disabling the control also remove the validations.
$("#myinput").attr('disabled', disabledValue);
Assuming you use default unobtrusive validation, You could use some javascript to remove rules on client side. Take a look at Plugins/Validation/rules
To achieve this goal in the given scenario, we need to make two tweaks.
Client Side
To disable client side validation, we need to disable it by force.
#Html.EditorFor(model => model.Password, new { htmlAttributes = new { #data_val = "false" , #class = "form-control"} })
Notice the #data_val= “false”. It will disable the validation on this field.
Server Side (In Action)
When the model is validated on the post action, ModelState.IsValid will always return false because password is not provided. Here we have to provide the current password to the model and Re-validate the model.
var userObj = db.Users_Info.Where(a => a.Id == users_Info.Id).FirstOrDefault();
if (String.IsNullOrEmpty(users_Info.Password))
{
users_Info.Password = userObj.Password;
}
ModelState.Clear();
TryValidateModel(users_Info);
Let me explain, first we retrieve current information saved in the database which we are using later to assign to current model if password is not provided. The last two lines actually reset the ModelState to return updated result on ModelState.IsValid.
I ran into trouble with data_val="true". I had a sequence of radio buttons tied to a single property in my model. The data validation only worked when I applied data_val="true" to the first #Html.RadioButtonFor call.
In debugging this, I discovered you can also disable or alter individual rules on the client side by using data_rule_??. The rules can be found in the jquery validation documentation.
for example;
#Html.RadioButtonFor(m => m.Answer, "Yes", new { data_rule_Required = "false" });
#Html.TextBoxFor(m => m.Answer, new { data_rule_minlength = "10" }
If you want to remove validations in MVC5 client-Side you need to do the following:
$("#Email").rules("remove", {
"required",
"minlength",
"email"
}
});
Related
I am exploring writing single page applications with React on the front end and .NET on the backend (and I am new to both, so apologies if the questions seem simple!). I am using Visual Studio for Mac.
In order to start simple, all my backend code does is returns "Hello, (name of person)" e.g. "Hello, Bob". However, I want the name of the person to be whatever a user inputs into the React form on the web page.
.NET Controller:
namespace TestingReactDotNet.Controllers
{
[Route("api/[controller]")]
public class DisplayNameController : Controller
{
[HttpGet, Route("Greeting")]
public string Greeting(string name)
{
var greeting = "Hello" + name;
return greeting;
}
}
}
React file (DisplayName.js):
import React, { Component } from "react";
export class DisplayName extends Component {
state = {
name: "",
greeting: ""
};
updateInput(key, value) {
this.setState({
[key]: value
});
}
calculate(){
fetch("api/DisplayName/Greeting")
.then(response => response.text())
.then(data => {
this.setState({ greeting: data });
});
<h1>this.state.greeting</h1>
}
render() {
return (
<center>
<div className="App">
<div>
Type a word...
<br />
<input
type="text"
placeholder="Type word here ..."
value={this.state.name}
onChange={e => this.updateInput("name", e.target.value)}
/>
<button onClick={() => this.calculate()}>Submit</button>
<br />
</div>
</div>
</center>
);
}
}
My question is:
How do I get the frontend and backend to talk to each other here, so that whatever name the user inputs into the form, the DisplayName method can use in order to return the string which is then displayed on the page?
I appreciate it's probably easier just to do this using React alone, but I am really trying to ensure that I can use React and .NET together, so any advice is really appreciated.
Thanks :)
Your code is excellent. But there is only one problem: when you got the result from the server, you do it nothing!
Look at your calculate() method:
calculate(){
fetch("api/DisplayName/Greeting")
.then(response => response.text())
.then(data => {
this.setState({ greeting: data });
});
<h1>this.state.greeting</h1>
}
What does the last line do? It creates a meaningless element that won't appear in any place! (also, you forgot the curly braces around this.state.greeting).
Instead, you should use the following approach: render the form if the it's not submitted yet, otherwise render the answer (it's just an example; you can render the both (if available) and more):
calculate(){
fetch("api/DisplayName/Greeting")
.then(response => response.text())
.then(data => {
this.setState({ greeting: data });
});
}
render() {
if ('' !== this.state.greeting) {
// We already submitted the form, show welcome message
return <h1>{this.state.greeting}</h1>
} else {
// As your code
// ...
}
}
Edit:
After your comment, I noticed why your server doesn't receive the name: it's simple - you don't send it!
So, How To Send Parameters To ASP.NET Backend With The Fetch API?
I'll discuss it shortly; it is very wide subject. I you want a comprehesive explanation, google for "ASP.NET Core Model Binding" and "js fetch api".
Let's start with the client side:
There are some ways to send data using fetch(): the simplest are query string (http://...?w=x&y=z) and using the body option, which accepts string that may be json, for example.
But the body parameter not works for get request. So, we'll use query string. Your case is simple enough to simply concatenate the strings, for more complex cases, see Setting query string using Fetch GET request.
Update your client code as follows:
// ...
fetch('/api/DisplayName/Greeting?name=' + encodeURIComponent(this.state.name))
// ...
(We're using encodeURIComponent() which encodes a string to be inside the query string).
In the server side, this is automatically, very luickly!
But to learn: what's happen?
In ASP.NET, there is a concept called Model Binding: The ability to take parameters from the request and make them parameter of the controller method.
There are some builtin model binders in ASP.NET (you can build your own, but in most cases you don't), our important is:
1) Query string model binder, which takes the query string parameters and pass them as arguments with the same name to the controller.
2) JSON model binder, which if the content-type is json, parses it and pass it too.
There are much more model binders.
These binders can handle also arrays (including the query string (in special form)!), nested object properties (which can lead to overposting attack) and more.
So, the only thing you need is to pass the parameter, then the model binder will do all the work automatically for you!
I have the following code:
#Html.EditorFor(model => model.AssociatedCard, new { htmlAttributes = new { #class = "form-control", #id="addCardBox", #onkeydown="test()"} })
Notice the #onkeydown="test()" attribute. This works fine, on keydown, a JS function is called.
But I want to send the key that was pressed, to the server, and save it to the database.
How can I send this key to the server, so that I can handle it in the C# code?
In asp.net, we can do something like this (which will allow me to bind to the TextBox):
<asp:TextBox id="txt" Runat="Server"></asp:TextBox>
At server side, I can then reach my textbox with the id=txt. Can I do something similar, without having to use ajax/JQuery?
Provided EditorFor is producing a textbox you can do the following as it will capture the input.
You appear to be on the right track given that you've created a strongly typed view, you just need to wrap your inputs in a form to post them to the server.
#using (Html.BeginForm("PostAction", "Home"))
{
//Put your foreach loop in here
}
Then in your controller
public class HomeController : Controller
{
[HttpPost]
public ActionResult PostAction(ViewModelType model)
{
//model.AssociatedCard should contain your value
}
}
Our application (MVC5) has some very complex validation that needs to be done server side (compound capacity checks, workflow validation, and more). The problem we are running into is that once server side validation fails and returns to the same view, the client side never submits values for any fields again (0 for int, empty strings, etc.)
Our general pattern is as follows:
public ActionResult PerformSomeAction()
{
var model = GetActionTemplate();
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult PerformSomeAction([Bind(Include = ActionTemplate.FIELDS)] ItemTemplate template)
{
string errorMessage;
if (ModelState.IsValid)
{
bool isValid = ValidateAndPerformAction(template, out errorMessage)
if(isValid)
return RedirectToAction("Action", "Controller");
}
// Reset non-bound fields from new template
var model = GetActionTemplate();
template.FieldValue = model.FieldValue
return View(template);
}
Our views don't have anything special in them, other than the fact that some of our editors are built with the Telerik Kendo library. However, the symptoms are seen for all controls, not just Kendo based ones.
A basic View layout for editing a field is as follows:
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-9">
#Html.Kendo().TextBoxFor(model => model.Name).HtmlAttributes(new
{
title = ModelMetadata.FromLambdaExpression(model => model.Name, ViewData).Description
})
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
Does anyone have any suggestions on what we are doing wrong?
Note: While it could be argued that this validation could be triggered through AJAX or other service calls, we would prefer to do it with the post implementation that we are using.
Update:
After further research, it appears this has to do with Kendo and not MVC. If I switch my View to be the following:
#Html.EditorFor(model => model.Volume)
Instead of:
#Html.Kendo().NumericTextBoxFor(model => model.Volume).HtmlAttributes(new
{
#class = "",
title = ModelMetadata.FromLambdaExpression(model => model.PreBoilVolume, ViewData).Description
}).Value(Model.Volume)
Everything appears to work fine. So somewhere in that Kendo statement it fails to rebind when returning from the post. It doesn't matter if I set the value again manually, it will never send it back in.
I guess it is time to get rid of some Kendo statements and go back to a more basic UI.
What you "want" to do is a very basic scenario for MVC with failed validation.
The first one that always catches people is with drop down lists. The posted model, does not contain the list of items, so has to be re-populated after failed validation, and then passed back into the view on return.
When I can't solve things like this I start commenting stuff out and work forwards. So in this case strip your model back to one non-ID property and see if it will work. Then you can try and track down the culprit.
It looks like the selectedvalues are not set on the model. Try change these lines of code:
var model = GetActionTemplate();
template.FieldValue = model.FieldValue
return View(template);
to something like this:
var model = GetActionTemplate();
model.Selectedvalues = template.FieldValue ?
return View(model);
I want to display error messages dynamically i.e, when the cursor moves out of the field but not when the form is submitted.
My requirement is like this:-
I have to design a registration form with some fields like name,address,phone number,email,password etc.
i designed it & saved the data successfully in DB but what i exactly required in the sense i have to display error messages dynamically without using "ajax" as i have already stated ...
My code is like this:-
View:-
<div class="venfor_line5" popText><label>#Resources.Resources.VendorReg_phoneNumber<img src="~/images/star.png" /> </label>
#Html.TextBoxFor(model => Model.MobileNumber, new { #class = "input" })
#Html.ValidationMessageFor(model => model.MobileNumber)</div>
<div class="venfor_line1" popText = #Resources.Resources.VendorReg_emailHintStr>
<label>#Resources.Resources.VendorReg_email<img src="~/images/star.png" /> </label>
#Html.TextBoxFor(model => Model.Email, new { #class = "input" })
#Html.ValidationMessageFor(model => model.Email)</div>
I have gone through many references but not found exactly what i am looking for.Any help would be greatly appreciated. can anyone guide me in resolving this issue.
You could use jQuery's focusOut() method to perform validation. Something like:
$('#elementId').focusOut(function(){
//do validation here
});
If you are using unobtrusive jQuery validation you could eagerly enable it so that it is automatically triggered onblur without requiring the user to submit the form:
$(document).ready(function () {
var settngs = $.data($('form')[0], 'validator').settings;
settngs.onfocusout = function (element) { $(element).valid(); };
});
I have a view that displays a list of comments. It does this via the DisplayTemplate. All I have to do is something like #Html.DisplayFor(x => x.BlogPost.PostComments) and all the comments render appropriately.
There is a form at the bottom of the page to add a new comment. This page utilizes progressive enhancement. So if javascript is disabled then the form submits like normal, adds the comment to the database, then redirects to the action that renders the blog post. However, if javascript is available then jQuery hijacks the form's submit and makes the post via ajax. Well because the comment markup is in a display template, I don't know how to return it from the action method so that jQuery can drop it on the page.
I know how to do this with partial views. I would just have the action method return the right partial view and jquery would append the response to the comment container on the page.
Before I go chopping out my display template in favor of a partial view, is there a straight forward way that I'm missing to send back a display template from the controller?
Here is my action method:
public ActionResult AddComment(PostComment postComment)
{
postComment.PostedDate = DateTime.Now;
postCommentRepo.AddPostComment(postComment);
postCommentRepo.SaveChanges();
if (Request.IsAjaxRequest())
return ???????
else
return RedirectToAction("BlogPost", new { Id = postComment.BlogPostID });
}
When the page loads it doesn't need to worry about it because it uses the templates in the standard way:
<div class="comments">
#Html.DisplayFor(x => x.BlogPost.BlogPostComments)
</div>
I just want to know how I might send a single comment that utilizes the display template back to jQuery.
You may try returning the partial HTML representing the newly posted comment:
if (Request.IsAjaxRequest())
{
return PartialView(
"~/Views/Shared/DisplayTemplates/Comment.cshtml",
postComment
);
}
and on the client side append this comment to the comments container:
$.post('#Url.Action("AddComment")', { ... }, function (result) {
$('#comments').append(result);
// or $('#comments').prepend(result); if you want it to appear on top
});
Does this question give you what you are looking for? Seems to indicate that you can call a HTML helper from an action.
Create a partial view /Shared/DisplayTemplate.cshtml with the following razor code:
#Html.DisplayFor(m => Model)
Then in your controller (or preferably in a base controller class) add a method along these lines:
protected PartialViewResult PartialViewFor(object model)
{
return PartialView("DisplayTemplate",model);
}
In the OP's case then:
public ActionResult AddComment(PostComment postComment)
{
postComment.PostedDate = DateTime.Now;
postCommentRepo.AddPostComment(postComment);
postCommentRepo.SaveChanges();
if (Request.IsAjaxRequest())
return PartialViewFor(postComment);
else
return RedirectToAction("BlogPost", new { Id = postComment.BlogPostID });
}