I'm having problems trying to validate a drop down list, I've looked at similar questions on here and tried the suggestions but still no luck. One I haven't tried is making my Benefit Id nullable, but is that a good idea? many thanks
Model I'm trying to validate:
public class Benefit
{
public int Id { get; set; }
public string Name { get; set; }
}
View model:
public class LookupVm
{
public SelectList Benefits { get; set; }
}
Controller set up:
var model = new LookupVm
{
Benefits = new SelectList(_repository.Benefits.OrderBy(n => n.Name).ToList(), "Id", "Name")
}
The view:
#Html.DropDownListFor(benefits => Model.Benefits.SelectedValue, Model.Benefits, "-Select-")
#Html.ValidationMessageFor(benefits => Model.Benefits.SelectedValue)
You can add a SelectedBenefit property to you view model
public class LookupVm
{
public int SelectedBenefit { get; set;}
public SelectList Benefits { get; set; }
}
Then add on top of the view
#model LookupVm
And then dropdown list must be something like this:
#Html.DropDownListFor(model => model.SelectedBenefit, model.Benefits, "-Select-")
#Html.ValidationMessageFor(model => model.SelectedBenefit)
You will get the selected id on SelectedBenefit property and it will be a required field.
Related
Model
public class Customer
{
public string Name {get;set;}
public List<Product> Products {get;set;}
}
public class Product
{
public string Name {get;set;}
public ProductType {get;set;}
public IEnumerable<ProductType> ProductTypeList { get; set; }
}
Controller
[HttpGet]
public ActionResult Index(int id)
{
var customer = GetCustomer(id);
return View(customer);
}
View
#model Customer
#Html.TextBoxFor(x=>x.Name);
#for (int i = 0; i < Model.Products.Count; i++)
{
#Html.TextBoxFor(x => x.Products[i].Name)
#Html.TextBoxFor(x => x.Products[i].Price)
#Html.DropDownListFor(x => x.Products[i].ProductType, Model.ProductTypeList)
}
Result:
The name and price of the products in HTML are displayed correctly but the <select> does not have correct ProductType selected (the first item is selected even though model has other value).
When I submit the form the value is bound and when validation return the form is is also bound to selected value.
The only issue is that DropDownList selected value is not bound when the page is loaded first time.
I think the problem lies with the ProductType:
#Html.DropDownListFor(x => x.Products[i].ProductType, Model.ProductTypeList)
It appears to be a complex type.
Try changing it to this instead:
public class Product
{
public string Name {get;set;}
public string SelectedProductType {get;set;}
public IEnumerable<ProductType> ProductTypeList { get; set; }
}
In the above, SelectedProductType would be the Id of your ProductType.
Then setting it like this:
#Html.DropDownListFor(x => x.Products[i].SelectedProductType, Model.ProductTypeList)
Something very simple but I am looking for the best way to do it. I have a Movie entity, each Movie can be in one Language only(a lookup table with English, French,etc...). Now I'm trying to load all the available languages in the lookup in the Movie Create Page, the Movie View Model:
namespace Project.ViewModels {
public class Movie {
[Key]
public int ID { get; set; }
public string Title { get; set; }
public string Rating { get; set; }
public string Director { get; set; }
public string Plot { get; set; }
public string Link { get; set; }
public string Starring { get; set; }
public int DateCreated { get; set; }
public string Genre { get; set; }
[Display(Name = "Language")]
public int LanguageID { get; set; }
// Navigational Properties
public virtual MovieLanguage Language { get; set; }
}
}
The MovieLanguage View model:
namespace MAKANI.ViewModels {
public class MovieLanguage {
[Key]
public int ID { get; set; }
public string Language { get; set; }
public virtual ICollection<Movie> Movies { get; set; }
}
}
The controller action:
public ActionResult MovieCreate() {
using (MAKANI.Models.Entities db = new MAKANI.Models.Entities()) {
List<Models.MoviesLanguages> enLanguages = db.MoviesLanguages.ToList();
IEnumerable<SelectListItem> selectList =
from m in enLanguages
select new SelectListItem {
Text = m.Language,
Value = m.ID.ToString()
};
ViewBag.SelectLanguage = selectList.ToList();
return View();
}
}
And in the View page i have
<div class="editor-field">
#Html.DropDownList("Language", ViewBag.SelectLanguage);
</div>
Howver I am getting this error in the View:
'System.Web.Mvc.HtmlHelper' has no applicable method named 'DropDownList' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax
Not sure what the problem might be?
Another questions regarding this approach:
Should a create a view model for the MovieLanguage entity in the first place, knowing that it servers only as a lookup table(so it doesnt require any Create/Edit/Delete action, Only List/Read maybe), should I be depending on the EF entities directly in that case?
Have a Languages Collection Property in your Movie ViewModel and a SelectedLanguage Property to get the selected Language ID when the form submits. It is not necessary that your ViewModel should have all the properties like your domain model. Have only those properties which the View needs.
public class Movie
{
public int ID { set;get;}
public string Title { set;get;}
//Other Relevant Properties also.
public IEnumerable<SelectListItem> Languages { set;get;}
public int SelectedLanguage { set;get;}
public Movie()
{
Languages =new List<SelectListItem>();
}
}
Now in your GET Action, Create an object of your Movie ViewModel and set the Languages Collection property and send that to the View. Try to avoid using ViewBag for passing data like this. ViewBag makes our code dirty.Why not use the strongly typed ViewModels to its full extent ?
public ActionResult CreateMovie()
{
var vm=new Movie();
// TO DO : I recommend you to abstract code to get the languages from DB
// to a different method so that your action methods will be
// skinny and that method can be called in different places.
var enLanguages = db.MoviesLanguages.ToList();
vm.Languages= = from m in enLanguages
select new SelectListItem {
Text = m.Language,
Value = m.ID.ToString()
};
return View(vm);
}
And in your view which is strongly typed to our Movie ViewModel, use the DropDownListFor Hemml helper method
#model Movie
#using(Html.Beginform())
{
#Html.DropDownListFor(x => x.SelectedLanguage,
new SelectList(Model.Languages, "Value", "Text"), "Select Language")
<input type="submit" />
}
Now when you post the form, you will get the selected languageId in the SelectedLanguage Property of your ViewModel
[HttpPost]
public ActionResult CreateMovie(Movie model)
{
If(ModelState.IsValid)
{
//check model.SelectedLanguage property here.
//Save and Redirect (PRG pattern)
}
//you need to reload the languages here again because HTTP is stateless.
return View(model);
}
I am constructing a page using Editor Templates and composition. My view model contains properties which are themselves view models. E.g.
public class ParentModel
{
public boolean SomeCheckBox { get; set; }
public ChildModel Child { get; set; }
}
public class ChildModel
{
[Required]
public string SomeString { get; set; }
[Required]
public string SomeOtherString { get; set; }
}
I would like the data annotation validation to kick in on the child only if the property SomeCheckBox on the parent is true.
I've seen a RequiredIf custom validation attribute elsewhere on stackoverflow, however it only works when the condition is a value of the same view model. I need something that can check the parent, or indeed a property on an ancestor.
My temporary hack is to clear the ModelState errors on postback if the checkbox isn't true.
I've also had to write some custom javascript so that the client browser suppresses the validation if the checkbox isn't ticked.
The real example is much more complicated than this but hopefully it's clear from the above simplified example what I'm after.
What would be nice is an attribute on the parent view model that is something like
public class ParentModel
{
public boolean SomeCheckBox { get; set; }
[SuppressValidationIf("SomeCheckBox", false)]
public ChildModel Child { get; set; }
}
Any ideas?
This excellent example perfectly illustrates the limitations of doing declarative validation which is what data annotations are.
It's for this reason that I would recommend you using an imperative approach for your validation rules as you can handle many scenarios. FluentValidation.NET is a great example of a library which would have rendered this validation scenario a piece of cake.
Let me illustrate how it could handle this scenario:
We start by defining validators for our child and parent models:
public class ChildModelValidator : AbstractValidator<ChildModel>
{
public ChildModelValidator()
{
RuleFor(x => x.SomeString).NotEmpty();
RuleFor(x => x.SomeOtherString).NotEmpty();
}
}
public class ParentModelValidator : AbstractValidator<ParentModel>
{
public ParentModelValidator()
{
RuleFor(x => x.Child)
.SetValidator(new ChildModelValidator())
.When(x => x.SomeCheckBox);
}
}
Notice how the child validator is included based on the value of the SomeCheckBox property on the parent? Now your models would look like this:
[Validator(typeof(ParentModelValidator))]
public class ParentModel
{
public bool SomeCheckBox { get; set; }
public ChildModel Child { get; set; }
}
public class ChildModel
{
public string SomeString { get; set; }
public string SomeOtherString { get; set; }
}
And that's all.
You simply install the FluentValidation.MVC3 NuGet, add the following line in Application_Start:
FluentValidationModelValidatorProvider.Configure();
and now you work as usual:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new ParentModel
{
Child = new ChildModel()
});
}
[HttpPost]
public ActionResult Index(ParentModel model)
{
return View(model);
}
}
and a view:
#model ParentModel
#Html.ValidationSummary(false)
#using (Html.BeginForm())
{
#Html.CheckBoxFor(x => x.SomeCheckBox)
#Html.EditorFor(x => x.Child.SomeString)
#Html.EditorFor(x => x.Child.SomeOtherString)
<button type="submit">OK</button>
}
If the checkbox is checked the child validator will kick in and require the 2 properties.
I would like to try this
Telerik Grid throw a circular reference exception when I try to use an entityframework poco class into his binding. The code mentioned in the link propose to replace the json serializer used by Telerik with the NewtonSoft one. But Telerik Grid never call the create method from CustomGridActionResultFactory injected into the Grid. Does someone know the problem about this code (link above)?
There is a code library project which shows how to create a custom GridActionResultFactory. You may find it helpful.
You should use a "special" View Model for the TelerikGrid.
For examle if you use a Database Model something like this
public class Master {
public int Id { get; set; }
public string Name { get; set; }
public List<Detail> Details { get; set; }
}
public class Detail {
public int Id { get; set; }
public string Name { get; set; }
public Master Master { get; set; }
}
You need to create View Model as given below
public class MasterView {
public int Id { get; set; }
public string Name { get; set; }
}
public class DetailView {
public int Id { get; set; }
public string Name { get; set; }
public int MasterId { get; set; }
}
If you like me, think that creating a separate view models just for the telerik grid is overkill, you could feed your own Json to the grid created from a list of anonymous objects:
//This goes in the View:
Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
// your column mappings go here
})
.DataBinding(dataBinding => dataBinding.Ajax().Select("_yourMethodReturningJson", "YourControllerName", new { yourJsonMethodParameter = yourViewModel.someField }))
.Pageable()
.Sortable()
.Render();
And the Json method in your controller looks like this:
public JsonResult _yourMethodReturningJson(YourType? yourJsonMethodParameter)
{
var list = database.SomeCollection.Select(x => new
{
SomeColumnName = x.SomeField
});
return Json(list, JsonRequestBehavior.AllowGet);
}
Your could use a better Json library here if you like: http://james.newtonking.com/pages/json-net.aspx
I am trying to understand why my Html.ListBoxFor() is not highlighting current selected items when the view loads.
I have a database model:
public class Issue
{
[Key]
public int IssueId { get; set; }
public int Number { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public virtual ICollection<Creator> Creators { get; set; }
}
public class Creator
{
[Key]
public int CreatorId { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Issue> Issues { get; set; }
}
public class Icbd : DbContext
{
public DbSet<Issue> Issues { get; set; }
public DbSet<Creator> Creators { get; set; }
}
I then have an editing model:
public class IssueEditModel
{
public Issue Issue { get; set; }
public IEnumerable<Creator> Creators { get; set; }
public IEnumerable<Creator> SelectedCreators { get {return Issue.Creators;} }
}
Then, in my controller I populate IssueEditModel:
public ActionResult EditIssue( int id = 0 )
{
IssueEditModel issueEdit = new IssueEditModel{
Creators = db.Creators.ToList(),
Issue = new Issue{ Creators = new List<Creator>()},
};
if (id > 0)
{
issueEdit.Issue = db.Issues.Include("Creators").Where(x => x.IssueId == id).Single();
}
return View(issueEdit);
}
This populates all objects correctly (as far as I can tell, anyway.) In my View, I am writing a listbox like this:
<%: Html.ListBoxFor(
x => x.SelectedCreators,
new SelectList(
Model.Creators,
"CreatorId",
"LastName"
)
)%>
This lists all the options correctly, but I cannot get the currently select items to highlight. I almost want to write my own Html Helper because this is such a simple operation, I don't understand why this is being so difficult.
Why wont the Html Helper highlight the current items?
You need a list of scalar types as first argument to the ListBoxFor helper which will map to the creator ids that you want preselected:
public class IssueEditModel
{
public IEnumerable<Creator> Creators { get; set; }
public IEnumerable<int> SelectedCreatorIds { get; set; }
}
and then:
IssueEditModel issueEdit = new IssueEditModel
{
Creators = db.Creators,
SelectedCreatorIds = db.Creators.Select(x => x.CreatorId)
};
and in the view:
<%: Html.ListBoxFor(
x => x.SelectedCreatorIds,
new SelectList(
Model.Creators,
"CreatorId",
"LastName"
)
) %>
The example submitted by Darin is ALMOST correct, but there is a slight error. Presently, his example will preselect ALL creators! However, the desired result is to only preselect the creators associated with a particular instance of Issue.
So this:
IssueEditModel issueEdit = new IssueEditModel
{
Creators = db.Creators,
SelectedCreatorIds = db.Creators.Select(x => x.CreatorId)
};
Should be this:
IssueEditModel issueEdit = new IssueEditModel
{
Creators = db.Creators,
SelectedCreatorIds = CurrentIssue.Creators.Select(x => x.CreatorId)
};
Where CurrentIssue is an instantiation of the Issue class (presumably previously populated from the datastore).