What would be the best way to for a razor view to handle multiple models? for an MVC3 application.
I have two models, both similar, but the Postcode field is required for one model and not for another
public class IrelandPostcodeLookupViewModel , IWithProgress
{
readonly Progress _Progress = new Progress(Step.Delivery);
public Progress Progress
{
get { return _Progress; }
}
[Required(ErrorMessage = "Please enter your house number or name")]
[DisplayName("House number or name")]
public string HouseNumber { get; set; }
[StringLengthWithGenericMessage(50)]
[DisplayName("Eircode")]
public string Postcode { get; set; }
}
public class PostcodeLookupViewModel , IWithProgress
{
readonly Progress _Progress = new Progress(Step.Delivery);
public Progress Progress
{
get { return _Progress; }
}
[Required(ErrorMessage = "Please enter your house number or name")]
[DisplayName("House number or name")]
public string HouseNumber { get; set; }
[StringLengthWithGenericMessage(50)]
[Required(ErrorMessage = "Please enter your postcode")]
[DisplayName("PostCode")]
public string Postcode { get; set; }
}
In the controller I want to use a particular view model depending on a country I am passed. Something like
public virtual ActionResult PostcodeLookup(string country)
{
if (country == Country.UnitedKingdom)
return View(new PostcodeLookupViewModel());
else
return View(new IrelandPostcodeLookupViewModel());
}
I was handling this in the view with
#model dynamic
The problem I have with this is my view contains partial views
#Html.Partial("~/Views/Shared/_Progress.cshtml", Model.Progress)
and I run into the error 'HtmlHelper' has no applicable method named 'Partial' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched'
Can anyone advise how I can handle the Partial View?
Thanks
Because Model is dynamic, also Model.Progress produces a dynamic.
This is true for all properties and function calls on a dynamic object, no matter how deep you'd go.
To solve this you can typecast the Model.Progress object:
#Html.Partial("~/Views/Shared/_Progress.cshtml", (Progress)Model.Progress)
Related
i have a form wherein there is conditional textboxes.i have used mvc dataannotation client side on dropdown change i hide two textboxes data validation error is not fired but in controller i get model error in if (ModelState.IsValid).How can i do condtional handling of data annotation in client side only.I dont want to use fullproof validation or other third party.
i tried removing the data-val-* attributes using jquery still getting error in controller.refer image if i select asset type laptop then sim plan and price is hidden dataannotation dont fire which is correct but get error on controller.
Model:
[Required(ErrorMessage = "Please Enter Make")]
public string Make { get; set; }
[Required(ErrorMessage = "Please Enter Model")]
public string Model { get; set; }
[Required(ErrorMessage = "Please Enter Sim Plan")]
public string SimPlan { get; set; }
[Required(ErrorMessage = "Please Enter Price")]
public decimal? Price { get; set; }
there's a much better way to add conditional validation rules in MVC3. Have your model inherit IValidatableObject and implement the Validate method:
public class Person : IValidatableObject
{
public string Name { get; set; }
public bool IsSenior { get; set; }
public Senior Senior { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (IsSenior && string.IsNullOrEmpty(Senior.Description))
yield return new ValidationResult("Description must be supplied.");
}
}
see more of a description at http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx
If you trying to manually clear validation error which u have not used in your view than i would suggest you clear them before checking modelstate.
Ex:
[HttpPost]
public ActionResult Register(Model objModel )
{
foreach (string Key in ModelState.Keys)
{
if ((Key.Equals("Email")) || (Key.Equals("Password")))
{
ModelState[Key].Errors.Clear();
}
}
if (ModelState.IsValid)
{
//Do the work
}
}
In the above example i have passed the values in Model and i didn't pass any value for Email and password, so in controller i am clearing those Key which is present in ModelState.
I have an ASP.NET MVC4 application in which at a certain point there is a page (Index) where a user can select an item from a DropDownList. After submitting this, the controller will return a different PartialView-name to another View (Create), based on the selected item from the list. Each partial has its own ViewModel and when the PartialView is sent from the Controller to Create-View it is rendered correctly. To achieve this I made a general ViewModel and several other viewmodels which derive from this general ViewModel. The Create-view has the general ViewModel as Model and the Partials, which will be rendered in the Create-view, have the matching derived types as Model.
But here's the problem, when I submit the form on the PartialView I have to retrieve the right ViewModel back in the Controller. Accepting the general ViewModel as a parameter won't work as I won't be able to down-cast it to the right ViewModel then. Here's some example code that I have:
ViewModels:
public class PropertyViewModel
{
public string ViewName { get; set; }
public String Name { get; set; }
public String Description { get; set; }
}
public class IntegerViewModel : PropertyViewModel
{
public int MinValue { get; set; }
public int MaxValue { get; set; }
}
public class TextViewModel : PropertyViewModel
{
public int MaxLength { get; set; }
}
Controller:
public ActionResult Create(String partialName)
{
var model = GetViewModelFromName(partialName);
return View(model);
}
[HttpPost]
public ActionResult Create(???)
{
//What to do here and what kind of parameter should I expect?
}
Is there a 'clean' way to do this? And does anyone know how to achieve this?
Update:
I have a solution that seems to work. In the PartialView I set the actionName and the controllerName of the form, like this:
#using (Html.BeginForm("CreateIntegerProperty", "Property")) {
//Formstuff...
}
#using (Html.BeginForm("CreateTextProperty", "Property")) {
//Formstuff...
}
And in my Controller I have all the different Actions (one for each of the PartialViews). This seems to work. Now is this a clean way to do this? If anyone comes up with a better idea, please do let me know!
If your solution works then go with it. It seems fine to me. The only issue would arise if you were bothered about having the same URL for each action.
You could enhance it a tiny bit by adding the Action and Controller names to the base ViewModel if you wanted to, like this:
public class PropertyViewModel
{
public string ViewName { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Controller { get; set; }
public String Action { get; set; }
}
And then doing this:
#using (Html.BeginForm(Model.Action, Model.Controller)) {
//Formstuff...
}
This would be worth doing if it meant you could now use the same View (or partial view, or whatever) for the form.
If you DO want the same URL for each action, then one way is to override OnModelBinding, but I probably wouldn't bother personally.
I defined a model like this
public class Planilla
{
[Key]
public int IDPlanilla { get; set; }
[Required(ErrorMessage = "*")]
[Display(Name = "Dirección de Negocio")]
public int IDDireccionDeNegocio { get; set; }
[Required (ErrorMessage = "*")]
public string Nombre { get; set; }
[Display(Name = "Descripción")]
public string Descripcion { get; set; }
public bool Activo { get; set; }
[ScriptIgnore]
public virtual DireccionDeNegocio DireccionDeNegocio { get; set; }
}
And I have a method in my controller that returns the first element of this model
[HttpPost]
public ActionResult GetElements(string IDCampana)
{
Planilla query = db.Planillas.First();
return Json(query);
}
My problem is when I invoke this method from client side throws an error that say's
circular reference is detected trying to serialize
System.Data.Entity.DynamicProxies.Planilla_7F7D4D6D9AD7AEDCC59865F32D5D02B4023989FC7178D7698895D2CA59F26FEE
Debugging my code I realized that the object returned by the execution
of the methodFirstit's a
{System.Data.Entity.DynamicProxies.Planilla_7F7D4D6D9AD7AEDCC59865F32D5D02B4023989FC7178D7698895D2CA59F26FEE}
instead a Model of my namespace like
Example.Models.DireccionDeNegocio`.
Why am I doing wrong?? Because I tried with other models and work's well
Use view models, that's the only advice I can give you. Never pass domain models to your views. It's as simple as that. And if you respect this simple rule and fundamental rule in ASP.NET MVC applications you will never have problems. So for example if you need only the id and the description in your view:
[HttpPost]
public ActionResult GetElements(string IDCampana)
{
Planilla query = db.Planillas.First();
return Json(new
{
Id = query.IDPlanilla,
Description = query.Description
});
}
Notice that in this case the anonymous object serves as view model. But if you really wanted to do things properly you would write your view model:
public class PlanillaViewModel
{
public int Id { get; set; }
public string Description { get; set; }
}
and then:
[HttpPost]
public ActionResult GetElements(string IDCampana)
{
Planilla query = db.Planillas.First();
return Json(new PlanillaViewModel
{
Id = query.IDPlanilla,
Description = query.Description
});
}
By the way Ayende wrote a nice series of blog posts about this.
System.Data.Entity.DynamicProxies.* is the Entity Framework proxy namespace. Your DbContext creates your entities as such to support lazy loading and change tracking. This isn't your problem. The problem likely lies in a circular association.
I have a class that requires another class to be specified, but I don't want the MVC ModelState validator to check whether the secondary model is valid. Is this possible?
Here's a brief overview:
My entities look something like this:
public class WidgetType
{
public long Id { get; private set; }
[Required]
public string Name { get; set; }
...
}
public class Widget
{
public long Id { get; private set; }
[Required]
public string Name { get; set; }
[Required]
public WidgetType WidgetType { get; set; }
...
}
I have them encapsulated in a WidgetViewModel class that I'm passing to/from the View like this:
public class WidgetViewModel
{
public Widget Widget { get; set; }
public ICollection<WidgetType> WidgetTypes
{
get
{
return _repository.GetWidgets();
}
}
...
}
My view looks something like this:
...
#Html.DropDownListFor( m => m.Widget.WidgetType.Id, new SelectList( new EquipmentViewModel().EquipmentTypes, "Id", "Name" ) )
...
All of this works except for validation. ModelState.IsValid is always false because "Widget.WidgetType.Name" is required. I need the user to select a WidgetType, but I don't want ModelState to be validated deeper than "Widget.WidgetType.Id" (which should be all that Widget needs for its foreign key?).
Is there a better way to do this? I feel like there should be some way to validate without recursively inspecting deeper into the properties, but I can't find it. What am I missing...?
public class WidgetViewModel
{
[Required]
public string Name { get; set; }
[Required]
public WidgetType WidgetTypeId { get; set; }
public SelectList WidgetTypes
{
get
{
//This should be popuplated in your controller or factory not in the view model
retun new SelectList{ _repository.GetWidgets(),"Id","Name");
}
}
}
In your view
#Html.DropDownListFor( m => m.WidgetTypeId, Model.WidgetTypes)
And in your controller
public ActionResult Create(WidgetViewModel model)
{
Widget widget = new Widget{
Name = model.Name,
WidgetType = yourManager.GetWidgetTypeByID(model.WigetTypeId);
};
yourManager.Create(widget);
//...
}
If all you need in your view is the WidgetID then you don't need to include the entire Widget in the WidgetViewModel. Just have property called WidgetID. View model classes should have only the data the is necessary for the view.
In the controller action method that is called when you submit the form, you can use the WidgetID to fetch the Widget object from the database if it is needed.
http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/ gives an example of partial validation
This question was inspired by my struggles with ASP.NET MVC, but I think it applies to other situations as well.
Let's say I have an ORM-generated Model and two ViewModels (one for a "details" view and one for an "edit" view):
Model
public class FooModel // ORM generated
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public int Age { get; set; }
public int CategoryId { get; set; }
}
Display ViewModel
public class FooDisplayViewModel // use for "details" view
{
[DisplayName("ID Number")]
public int Id { get; set; }
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Email Address")]
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")]
public string CategoryName { get; set; }
}
Edit ViewModel
public class FooEditViewModel // use for "edit" view
{
[DisplayName("First Name")] // not DRY
public string FirstName { get; set; }
[DisplayName("Last Name")] // not DRY
public string LastName { get; set; }
[DisplayName("Email Address")] // not DRY
[DataType("EmailAddress")] // not DRY
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")] // not DRY
public SelectList Categories { get; set; }
}
Note that the attributes on the ViewModels are not DRY--a lot of information is repeated. Now imagine this scenario multiplied by 10 or 100, and you can see that it can quickly become quite tedious and error prone to ensure consistency across ViewModels (and therefore across Views).
How can I "DRY up" this code?
Before you answer, "Just put all the attributes on FooModel," I've tried that, but it didn't work because I need to keep my ViewModels "flat". In other words, I can't just compose each ViewModel with a Model--I need my ViewModel to have only the properties (and attributes) that should be consumed by the View, and the View can't burrow into sub-properties to get at the values.
Update
LukLed's answer suggests using inheritance. This definitely reduces the amount of non-DRY code, but it doesn't eliminate it. Note that, in my example above, the DisplayName attribute for the Category property would need to be written twice because the data type of the property is different between the display and edit ViewModels. This isn't going to be a big deal on a small scale, but as the size and complexity of a project scales up (imagine a lot more properties, more attributes per property, more views per model), there is still the potentially for "repeating yourself" a fair amount. Perhaps I'm taking DRY too far here, but I'd still rather have all my "friendly names", data types, validation rules, etc. typed out only once.
I'll assume that your doing this to take advantage of the HtmlHelpers EditorFor and DisplayFor and don't want the overhead of ceremoniously declaring the same thing 4000 times throughout the application.
The easiest way to DRY this up is to implement your own ModelMetadataProvider. The ModelMetadataProvider is what is reading those attributes and presenting them to the template helpers. MVC2 already provides a DataAnnotationsModelMetadataProvider implementation to get things going so inheriting from that makes things really easy.
To get you started here is a simple example that breaks apart camelcased property names into spaces, FirstName => First Name :
public class ConventionModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
HumanizePropertyNamesAsDisplayName(metadata);
if (metadata.DisplayName.ToUpper() == "ID")
metadata.DisplayName = "Id Number";
return metadata;
}
private void HumanizePropertyNamesAsDisplayName(ModelMetadata metadata)
{
metadata.DisplayName = HumanizeCamel((metadata.DisplayName ?? metadata.PropertyName));
}
public static string HumanizeCamel(string camelCasedString)
{
if (camelCasedString == null)
return "";
StringBuilder sb = new StringBuilder();
char last = char.MinValue;
foreach (char c in camelCasedString)
{
if (char.IsLower(last) && char.IsUpper(c))
{
sb.Append(' ');
}
sb.Append(c);
last = c;
}
return sb.ToString();
}
}
Then all you have to do is register it like adding your own custom ViewEngine or ControllerFactory inside of Global.asax's Application Start:
ModelMetadataProviders.Current = new ConventionModelMetadataProvider();
Now just to show you I'm not cheating this is the view model I'm using to get the same HtmlHelper.*.For experience as your decorated ViewModel:
public class FooDisplayViewModel // use for "details" view
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
public int Age { get; set; }
[DisplayName("Category")]
public string CategoryName { get; set; }
}
Declare BaseModel, inherit and add another properties:
public class BaseFooViewModel
{
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Email Address")]
[DataType("EmailAddress")]
public string EmailAddress { get; set; }
}
public class FooDisplayViewModel : BaseFooViewModel
{
[DisplayName("ID Number")]
public int Id { get; set; }
}
public class FooEditViewModel : BaseFooViewModel
EDIT
About categories. Shouldn't edit view model have public string CategoryName { get; set; } and public List<string> Categories { get; set; } instead of SelectList? This way you can place public string CategoryName { get; set; } in base class and keep DRY. Edit view enhances class by adding List<string>.
As LukLed said you could create a base class that the View and Edit models derive from, or you could also just derive one view model from the other. In many apps the Edit model is basically the same as View plus some additional stuff (like select lists), so it might make sense to derive the Edit model from the View model.
Or, if you're worried about "class explosion", you could use the same view model for both and pass the additional stuff (like SelectLists) through ViewData. I don't recommend this approach because I think it's confusing to pass some state via the Model and other state via ViewData, but it's an option.
Another option would be to just embrace the separate models. I'm all about keeping logic DRY, but I'm less worried about a few redundant properties in my DTOs (especially on projects using code generation to generate 90% of the view models for me).
First thing i notice - you got 2 view models. See my answer here for details on this.
Other things that springs in mind are already mentioned (classic approach to apply DRY - inheritance and conventions).
I guess i was too vague. My idea is to create view model per domain model and then - combine them at view models that are per specific view. In your case: =>
public class FooViewModel {
strange attributes everywhere tralalala
firstname,lastname,bar,fizz,buzz
}
public class FooDetailsViewModel {
public FooViewModel Foo {get;set;}
some additional bull**** if needed
}
public class FooEditViewModel {
public FooViewModel Foo {get;set;}
some additional bull**** if needed
}
That allows us to create more complex view models (that are per view) too =>
public class ComplexViewModel {
public PaginationInfo Pagination {get;set;}
public FooViewModel Foo {get;set;}
public BarViewModel Bar {get;set;}
public HttpContext lol {get;set;}
}
You might find useful this question of mine.
hmm... turns out i actually did suggest to create 3 view models. Anyway, that code snippet kind a reflects my approach.
Another tip - i would go with filter & convention (e.g. by type) based mechanism that fills viewdata with necessary selectList (mvc framework can automagically bind selectList from viewData by name or something).
And another tip - if you use AutoMapper for managing your view model, it has nice feature - it can flatten object graph. Therefore - you can create view model (which is per view) that directly has props of view model (which is per domain model) whatever how much deep you want to go (Haack said it's fine).
These display names (the values) could perhaps be displayed in another static class with a lot of const fields. Wouldn't save you having many instances of DisplayNameAttribute but it would make a name change quick and easy to make. Obviously this is isn't helpful for other meta attributes.
If I told my team they would have to create a new model for every little permutation of the same data (and subsequently write automapper definitions for them) they'd revolt and lynch me. I'd rather model metadata that was, too some degree use aware. For example making a properties required attribute only take effect in an "Add" (Model == null) scenario. Particularly as I wouldn't even write two views to handle add/edit. I would have one view to handle the both of them and if I started having different model classes I'd get into trouble with my parent class declaration.. the ...ViewPage bit.