DropDownList Client Side Validation is validating when it should not be. (MVC3, Razor) - asp.net-mvc-3

I am still learning MVC3 and Razor, so this is perhaps a simple question.
On a view I have a DropDownList whose sole purpose is to help filter (via AJAX) a second drop down list:
#Html.DropDownList("Segments", "-- select segment --")
There is a Segments property of the ViewModel that is defined as:
public IEnumerable<SelectListItem> Segments { get; set; }
There is JavaScript that handles the change event for this DropDownList and populates another DropDownList with appropriate values. That other DropDownList is defined like this:
#Html.DropDownListFor(m => m.fafhProdRecId, Enumerable.Empty<SelectListItem>(), "-- select product recommendation --")
This all works fine until I submit. When I submit, I get a validation error on the Segments drop down list!
Now -- there should be absolutely NO validation on the segments DropDownList -- there shouldn't be any client side validation on EITHER drop down list, for that matter.
But when I try to submit, I get the validation error message back:
The value '1' is invalid.
I have no idea why this is happening.
I have no idea how to decorate the Segments property to say that it is NOT required.
I have no idea how to tell the unobtrusive javascript validator that it is, in fact, being quite obtrusive.

In your ViewModel class add [Bind(Exclude = "Segments")]
From: Using Data Annotations for Model Validation

make sure that your Model has fafhProdRecId as nullable, I imagine it's declared as:
public int fafhProdRecId { get; set; }
change this to:
public int? fafhProdRecId { get; set; }
hopefully, that should resolve the issue as this effectively makes the model field nullable (assuming the db field IS nullable too of course).

Related

kendo MVC wrapper grid. How do i specify the data type of a nested model field?

I have a project work work with hundreds of grids. On one grid in particular, the original designer created a view and added it as a nested model inside our driver model. This is a problem because Kendo seems to not be able to detect the field type of a field nested this way. When it can't, it assumes the field is a string.
In my particular case, I have two fields that contain dates that we want to filter on, but the date filtering is broken because of this.
I was able to find numerous examples of specifying it in the data source, but only for jquery grids (one example: https://docs.telerik.com/kendo-ui/knowledge-base/use-nested-model-properties ). I can't convert this to mvc because the methods aren't lining up.
Any idea on how to do this in mvc? Note: I also tried just changing the UI on the filter to date and that didn't work either. I got a stack trace claiming date needed to be a string.
As far as I understand the model passed to the Grid's field is structured like this:
public class OrderViewModel
{
public int OrderID
{
get;
set;
}
public CityModel ShipCity
{
get;
set;
}
}
public class CityModel
{
public int CityID
{
get;
set;
};
public string CityName
{
get;
set;
};
}
Then you can utilize the ClientTemplate configuration to get the CityName to populate the column:
columns.Bound(p => p.ShipCity).ClientTemplate("#=ShipCity.CityName#");
For more information refer to the Kendo UI Templates article
or
this forum answer that shows how to set a default to a nested field in a Grid's DataSource Model configuration.
Unfortunately, according to Telerik , the fact is that you simply can't do what I was trying to do. The solution is to flatten the model. I pulled the questionable fields into notmapped fields in the master model.
The relevant part of their response:
In MVC the nested properties are properties to another Model. As the Telerik UI Grid needs a flat structure of data, the nested properties could be shown with the help of a ClientTemplate, but they will always be string typed.
In order to achieve the desired behavior, I would recommend making the pointed Date fields - part of the Main Model and using them as flat data out of the box.
Link: https://www.telerik.com/forums/kendo-mvc-wrapper-grid-how-do-i-specify-the-data-type-of-a-nested-model-field
The caveat here is you can't filter a linq-for-queries datasource to a notmapped field because it doesn't exist in it but in my case, a .toList() didn't cause any performance problems because the dataset was small, with only a few hundred records.
However, you can edit the incoming DatasourceRequest object to modify the filters and sorts to point at the sub model, after your controller read is hit.

ASP.NET MVC Fluent Validation doesn't work on the client side for view model's property which is another type of class

I've an ASP.NET MVC application using Fluent Validation. Basically there is a view model Book which has an author as a property on it, like:
class Book {
string Title { get; set; }
Author Author { get; set; }
...
}
class Author {
string Name { get; set; }
...
}
On the view there is a field for the Book.Author.Name.
#Html.EditorFor(m => m.Author.Name)
There is Fluent Validation rule set up for the Book view model something like this
RuleFor(book => book.Author.Name).NotEmpty();
Then I find that this validation won't be fired for the Book.Author.Name field on client side. When inspecting the field's HTML markup, I found that no validation data attributes have been generated. Seems like Fluent Validation cannot handle the multiple level structure properly to generate the client side validation markups. But after it is posted back to the server, the server side validation fires as expected for this field. How can I get the validation fired on the client side?
Eventually to work around this problem, I had to create a custom editor template for the Author class, and a separate Fluent Validator for the Author class with all the rules I needed for the properties on the Author class. This approach worked perfect in this case. The client side validation could be fired as expected this time, and so does the server side as well.
By the way, I've also tried to create a partial view instead of a custom editor template for the Author class. The client side validation was able to be fired, but after post back the partial view couldn't be bound to the Book.Author for the controller's action method.

ASP.NET MVC 4 avoid generation of data-val-date for datetime

how can I avoid the generation of the html attribute "data-val-date" for the element created from a Datetime property?
The model:
public class RegisterModel
{
[Required]
[Display(Name = "Date of birth")]
public DateTime? DateOfBirth { get; set; }
}
The view:
#Html.LabelFor(m => m.DateOfBirth)
#Html.EditorFor(m => m.DateOfBirth)
In fact, I'm creating a three drop down lists element for selecting the date of birth, which don't give a value in a date format.
Some solutions I've seen, consisted in a work around: removing the validation with a javascript.
The solution I envisage is to split the DateTime property into three long one for each value (day, month, year).
Ok, this took me an afternoon of work... apparently mvc4 decided that it was time to render a data-val-date="Message" on EVERY datetime property on the viewmodel. I've tried to modify this default behaviour but didn't succeed.
This solved my problems:
$.validator.addMethod('date',
function (value, element) {
return true; // since MVC4 data-val-date is put on EVERY vm date property. Default implementation does not allow for multiple cultures...
});
You can also try to write your own editor template named "DateTime.cshtml" in your shared EditorFor folder, but don't use TextBoxFor there, because that one is polluted as well.
data-val-date is used by the validation system to validate the date. If you remove it, client-side validation won't work.
If that's what you want, then just disable client-side validation.
Add this to your application start in your global.asax file and the form should fire.
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

A `ViewModel` for each page (`Create.cshtml` and `Edit.cshtml`)?

Questions
There are actually two related questions:
Should I create a ViewModel for each page?
If you do not have problems in creating a single ViewModel class for the two pages (Create.cshtml and Edit.cshtml) how can I validate the ViewModel in different ways (depending on the page that is being used)
Source
ViewModel
public class ProjectViewModel
{
public string Name { get; set; }
public string Url { get; set; }
public string Description { get; set; }
}
Edit.cshtml
#using BindSolution.ViewModel.Project
#model ProjectViewModel
#{
ViewBag.Title = Model.Name;
}
#Html.EditorForModel()
Create.cshtml
#using BindSolution.ViewModel.Project
#model ProjectViewModel
#{
ViewBag.Title = "New Project";
}
#Html.EditorForModel()
ProjectValidator.cs
public class ProjectValidator : AbstractValidator<ProjectViewModel>
{
private readonly IProjectService _projectService;
public ProjectValidator(IProjectService projectService)
{
_projectService = projectService;
RuleFor(p => p.Name)
.NotEmpty().WithMessage("required field")
/*The validation should be made only if the page is Create.cshtml. That is, if you are creating a new project.*/
.When(p => p.??) //Problem Here!!
.Must(n => !_projectService.Exist(n)).WithMessage("name already exists");
RuleFor(p => p.Url)
.NotEmpty().WithMessage("required field");
}
}
Note that if the user is editing an existing project, validation of the property name should not be done again.
ProjectController.cs > Edit method
[HttpPost]
public ActionResult Edit(Guid projectID, ProjectViewModel model)
{
var project = _projectService.Repository.Get(projectID);
if (ModelState.IsValid && TryUpdateModel(project))
{
_projectService.Repository.Attach(project);
if (_projectImageWrap.Create(project) && _projectService.Repository.Save() > 0)
return AjaxRedirect("Index");
}
return View(model);
}
Notes
If I create a ViewModel for each page, there is a duplication of code since pages have the same properties.
Add a property on the ViewModel indicating what page it is being displayed does not solve my problem as to instantiate the ViewModel, I use AutoMapper.
To validate the data, I use FluentValidator.
Thank you all for your help!
My understanding is that there isn't a 1:1 correlation between ViewModels and Views. Oftentimes you will have a View that will not require a ViewModel to go alongside with it.
You will want to create a ViewModel if and only if you need a Model absolutely paralleled and tailored to a specific View. This will not be the case 100% of the time.
When the functionality / use case /validation is different between the pages I use different models. If its the exact same besides the presence of an ID or something similar I use the same model, and its also possible to just use the same view if the differences are pretty minor.
Since your validation is different, if I were doing it I would create two different models so that I could use the out of the box DataAnnotations, with your validation though it may not be required. You could also on the edit model have a readonly property for name since its not editable any longer.
For me the same object must have the same validation on every time, in main to ensure the consistence of the object, independently if it was created or edited.
i think that you should create only one validation, and edit your "exists" method to pass to verify if it is a new object or the current object in repository.
Personally, I don't have a problem with 2 view models, especially if (as Paul Tyng suggested) you use a base class for the fields that are common to edit and create scenarios.
However, if you really only want a single view model then you would either need to:
add a flag to the view model and use the When() method in your validator. Note though that this will not generate the appropriate client-side only validation
define a second validator and invoke the appropriate one from the controller (i.e. instead of the "automatic" validation)
Provide another view Edit.cshtml which will allow the user to edit the data for a selected item.
Create another view Query.cshtml which based on the ItemName will allow the users to query the Inventory table.
Perform the calculation for the total profit (numbersold times (saleprice-purchasecost). Display the total profit.
(BONUS) Create another view Sell.cshtml that will indicate the sale of an item. Adding one to NumberSold and subtract one from NumberInventory for the selected record.

Validating parameters passed through the URL

I am working on an ASP.Net MVC3 application and I'm having trouble understanding the "right way" to do the validation I'm looking for.
For example, consider a model that looks like this:
[Required]
[StringLength(10, MinimumLength = 10)]
[RegularExpression("[0-9]{10}")]
public string Id { get; set; }
[Required]
public string Value { get; set; }
If I have an Id of "2342" and try to POST back, the model mapping kicks in and registers an error because of the length validation. However, if perform a GET against /controller/2342, then MVC seems to happily create a model with this invalid state (ModelState.Valid will be true). I could create some validations in the controller to catch this, but it seems redundant.
What is the best way to do this?
Thanks!
Jacob
When you perform a GET, you are simply retrieving a model with a given ID. So there is no validation performed. If you really want to make sure that requested model IDs should be 10 numbers in length, you should define constraint in Global.asax:
routes.MapRoute(
"Product",
"Product/{productId}",
new {controller="Product", action="Details"},
new {productId = #"[0-9]{10}" }
);
There is nothing in the framework that by default validates a model on a GET request as validation isn't generally required at that time. If you do want to force a validation here, this was answered in this prior question
See:
Validate model on initial request

Resources