I am making a form with MVC and am using the [ControlType]For([expression]) helper methods (EG Html.TextBoxFor(...))
To bind the data to the controls I am using the following ViewModel:
public class UserViewModel
{
//The Model that will be used to bind data to the elements
public UserModel User { get; set; }
//Used to bind selectable options to DropDownLists
public SelectList DescentTypes { get; set; }
public SelectList GenderTypes { get; set; }
}
When using this the name of the controls get set to name="Property.SubProperty" (EG name="User.Id") but I would rather it show as name="Id" on my html form.
Is it possible to do this without having to write a lot of custom code so the framework can translate it back to the ViewModel (UserViewModel) or just the Model (User) itself?
I'd advise leaving the default naming in place unless you have a very good reason to alter it. IDs (which it appears your question is leaning towards) are more flexible.
Changing IDs
IDs aren't submitted with a form, so you can set them as desired without breaking model binding. By default, they are hierarchical, but you can override them inline:
#Html.TextBoxFor( o => o.UserName, new { id = "foo" } )
Of course, this is manual work.
If the big concern is external JS/CSS, I'd suggest using class names and data-* attributes in your (CSS/jQuery/whatever) selectors rather than IDs.
#Html.TextBoxFor( o => o.User.UserName, new { data_role="grand-total" } )
It's still manual, but it's descriptive and independent of an ID.
Sometimes I use a snippet of script in my views to initialize a larger JS class with data that is most easily available directly within the view. This lets the bulk of the script reside in an external file while allowing dynamic values to be used to initialize it. This is useful for more than just IDs.
Altering Generated Markup and Binding
For reference, let's say you wanted to change ID and name.
Write your own HtmlHelper extension methods to create the markup you want. You could probably wrap the existing methods that do not take an expression and pass explicit values to them to indicate the name that you want.
Write your own ModelBinder to map the raw form collection.
Determine a strategy for dealing with hierarchical objects (which is the main reason the naming convention exists in the first place).
Item #3 could be addressed by decorating properties to indicate how the naming should be performed and how model binding should map. This could become complicated quickly.
public class UserViewModel
{
// use this metadata to determine how to handle properties on this member
[Flatten]
public UserModel User { get; set; }
public SelectList DescentTypes { get; set; }
public SelectList GenderTypes { get; set; }
}
Alternatives
Flatten your view model by adding User's properties directly to it. It looks like your are composing your view model from domain model(s). This isn't usually a good idea. I'd suggest reading the pros/cons of binding directly to domain models.
Leave the naming alone. It really isn't hurting anything and it makes life easy. You can avoid ever directly working with names/IDs in your client code by using helper methods.
For example:
// JavaScript + Razor
var name = "#Html.NameFor( o => o.User.Id )";
alert(name);
Related
I have a lot of fields in my View that are masked with jquery MaskedInput. They're masked because the user need to see it with a mask, but I need to remove these masks before commiting the value in my database.
I don't want to mess up the code, since this is one of the most importants Views in my project.
So what's the best pratice to do this?
Suppose I have this Model:
public class MyViewModel {
public string MyMaskedProperty { get; set;}
}
And this code in View:
#Html.TextboxFor(x=> x.MyMaskedProperty, new { #class="myMask"} )
Should I:
Remove the mask on my View, using javascript, before the form is subimitted
Remove the mask on my Model, changing the get of MyMaskedProperty to return an unmasked value
Remove the mask on my Controller, since it need to be unmasked only from here and beyond
Something better than the 3 solutions above.
Thanks in advance!
Similar to the second option, you might simply add a read-only field to your view model:
public class MyViewModel {
public string MyMaskedProperty { get; set;}
public string MyUnmaskedProperty
{
get
{
// return an "unmasked" version of MyMaskedProperty
}
}
}
Even if you were to modify the value with JavaScript, you can't really implicitly trust that and would want to validate server-side anyway. And doing it in the controller isn't ideal because that's just procedural code that would need to be repeated all over the place. Following the advice to "keep your controllers light and your models heavy", this logic really belongs on the model.
The model itself can be constructed from any version of the data, really. As long as the information used to construct the model can effectively construct it, then the model can expose all sorts of operations and properties for manipulating and viewing that information.
What is the best practice for binding a list of items to an ItemsControl when using the MVVM pattern?
1. Bind a list of ViewModels
Load the items from the database, create the models and all viewmodels and then bind a list of viewmodels to the ItemsControl.ItemsSource:
public class MyMainViewModel
{
public List<PersonViewModel> Persons { get; set; }
}
2. Bind a list of Models
Load the items from the database, create the models and then bind a list of those models directly to the ItemsControl.ItemsSource:
public class MyMainViewModel
{
public List<Person> Persons { get; set; }
}
I think the answer here is really it depends.
Firstly, you need to assess whether your view needs to interact with your model in such a way that it would make sense for a view model to be wrapped around a particular model. Let's look at an example:
public class WebsiteModel
{
public string URL { get; set; }
}
Here I have a very simple model which represents a website, nothing too fancy. I can create a view model which will encompass all websites, like a one to many relationship:
public class WebsitesViewModel
{
//A list of websites.
public List<WebsiteModel> Websites { get; set; }
//The command I will use to navigate, where the object parameter will be the WebsiteModel.
public ICommand NavigateCommand { get; set; }
...
public void Navigate(WebsiteModel model)
{
...
}
Here I want my view to be able to navigate to the URL using the browser. My view model holds a list of models and my command takes care of navigation.
The next method I can create a view model to represent a single model, I would say this is a SOLID approach:
public class WebsiteViewModel
{
//The website model
public WebsiteModel Website { get; set; }
//The command I will use to navigate, no parameters needed.
public ICommand NavigateCommand { get; set; }
...
public void Navigate()
{
...
}
In this scenario, I'll need another view model which will expose a list of WebsiteViewModel to my view.
public List<WebsiteViewModel> Websites { get; set; }
The truth is there isn't really a best practice. Neither method really trumps the other. There are benefits to each method, however the method to choose really depends on the implementation. In this scenario, method 2, I would say is overcomplicated. However it's not uncommon for a view model to become very large very quickly, and the need to separate concerns will force you to create smaller classes, and even view models to wrap your models inside, making method 2 a viable option.
So to wrap up. Neither method is best practice.
The only "correct" way to do it, is to use ViewModels all the way.
While initially more work, it gives you more flexibility and less bugs later on.
Don't for get, when you a model should only be valid in it's bounded context and when you bind your ViewModel to the view, you have a leaky abstraction. The View becomes aware of model and each change to the model, will affect your View.
Further more, refactoring doesn't work within XAML. So if you name a model property via refactoring, your XAML will still bind to the old property. This doesn't give you a compile error and your bounded element will just remain empty (in best case) or crash (in worst case).
This can be quite hard to figure out and fix. Also as Scroog1 commented, it introduces memory leaks. May not be noticeable in small applications, but ones working with big data sets it can lead to out of memory exceptions.
You should utilize an automapping library to map from Model to ViewModel in cases that allows it, this will reduce some boilerplate code. But remember to avoid ViewModel to Model automappings, as it's discouraged.
You want to avoid that a change in your model influences code in a different bounded context, i.e. you don't want to expose every database or model change in a rest service, even if the change do not influence the given rest action.
The same strategy can be applied to the n-layer model (View, ViewModel, (Domain) Model layer, Services and Infrastructure)
I think there is no correct way, using models is the pragmatic and simpler way, using view models is more time consuming but more decoupled...
You should have a look at this article:
http://blog.alner.net/archive/2010/02/09/mvvm-to-wrap-or-not-to-wrap.aspx
also: http://www.codeproject.com/Articles/61147/MVVM-Creating-ViewModel-Wrap-your-business-object
I am building a site that displays forms for different vendors.
Each vendor have some common properties, but there are some properties specific to some vendors. For instance one vendor asks for customer's Title (ie Mr, Mrs...) while others don't.
Even more, some of the properties may have different behavior. In the Title example above, one vendor could have it as a free text, while another would have it as an option select.
For this, i have a base class as a model, that holds the most common properties, and for each vendor i will create classes inheriting from the base to add the vendor specific properties, or change their behavior (like adding Data Annotations).
So, i would like to create forms specific to each vendor. What i have done and it really works, is create the generic one in the standard Views, Models, Controllers folders and each Vendor will have his own Area.
Is this really the right approach? I have seen that areas are mostly for mini sites or for localization.
Any opinion would be appreciated.
I'd say your using the wrong tool for the job here (i.e. Areas), however, you aren't far off.
Instead of using a separate area, all you need to do here is introduce separate views per "vendor" type. You could create a base view and have each specific view inherit from that. In fact, you could probably get away with a single view e.g.
public class VendorViewModel
{
...
public CustomerViewModel Customer { get; set; }
}
public class CustomerViewModel
{
public string Title { get; set; }
public IEnumerable<string> TitleOptions { get; set; }
...
}
Given the above model, your view could look like
#model VendorViewModel
...
#if (Model.Customer != null)
{
// make title selection configurable
#if (Model.Customer.TitleOptions != null)
{
#Html.DropDownListFor(m => m.Customer.Title, new SelectList(m.Customer.TitleOptions, "[Select title]"))
}
else
{
#Html.EditorFor(m => m.Customer.Title)
}
...
}
This effectively builds your view based on a particular vendors requirements e.g.
Leave the Customer property null if you don't need to capture customer info
Leave the TitleOptions property null if you want to have free-form text entry for title
This is just a simple example of how you can build up your view dynamically, and all in a single view. You could tidy it up a little by moving the customer view stuff into it's own partial view and only rendering that if required e.g.
#if (Model.Customer != null)
{
#Html.RenderPartial("CustomerView", Model.Customer)
}
I’m tracing down an unexpected behavior in MVC3, having to do with how it gets model metadata.
I had previously talked to one of my developers about using the same EditorTemplate for some data which is collected in two different areas of the system. The data fields are almost identical, except for the [Required] attribute. In one page certain fields are required, in the other page they are not. Theoretically this can be accomplished by using a base model which has the common attributes on each field, and inheriting those models, overriding the properties and adding additional validation attributes. For example:
class BaseModel
{
[Display(Name=”My Label”)]
public virtual string MyLabel { get; set ;}
}
class RequiredModel : BaseModel
{
[Required]
public override string MyLabel { get; set ;}
}
Then the View can be strongly typed to BaseModel, and calls to #Html.EditorFor(m=>m.MyLabel) in the view should pick up the correct attributes, depending on whether the actual instance of the model is a BaseModel or RequiredModel.
That’s the theory.
And in fact, it works well if you use the “old” HTML helper, e.g. #Html.TextBox(“MyLabel”). Those call ModelMetadata.FromStringExpression(field), which correctly gets the metadata from RequiredModel if the concrete model instance is RequiredModel. The newer helper methods call ModelMetadata.FromLambdaExpression(expression), which does NOT correctly get the metadata from the correct concrete instance.
Is this a bug in MVC? Intentional behavior? Is there a workaround, or a better way to address this problem?
This is of course a trivial example, the actual code we're dealing with has about 20 fields with some complex business rules and interaction, which is the same on both pages EXCEPT for which fields are required.
That's the theory.
No, that's not the theory. At least not mine.
My theory is to use separate view models for each views because the requirements of your views are different. So you would have this:
public class UpdateViewModel
{
[Display(Name = "My Label")]
public string MyLabel { get; set ;}
}
and:
public class CreateViewModel
{
[Display(Name = "My Label")]
[Required]
public string MyLabel { get; set ;}
}
Personally that's what I would do. I would totally sacrify DRY into designing view models because the requirements of my view change often and I want to have total control.
Obviously in practice I don't even bother with doing validation using declarative DataAnnotations attributes. They limit me. I use FluentValidation.NET which addresses problems like yours in a pretty elegant manner (by simply defining two different validators for the same view model - in case you decide to violate my theory and use the same view model in different views).
Now feel free to downvote my answer. I have just given my 2¢.
I'm trying to figure out if its possible to have multiple inheritance in a view in ASP.Net MVC. Right now I'm trying to print out a list of entries from two different tables from my model in a single View. I have the following line at the top of my view:
Inherits="System.Web.Mvc.ViewPage<List<GEApproval.Models.CoursePrefix>>"
But I also want to include the table Course as follows:
Inherits="System.Web.Mvc.ViewPage<List<GEApproval.Models.Course>>"
I'm not sure how this can be accomplished, any help or suggestions are appreciated.
UPDATE:
Thank you all for your help, I went ahead and created a composite class as follows:
namespace GEApproval.Models
{
public class Listings: GEApproval.Models.CoursePrefix, GEApproval.Models.ICourse
{
public List<CoursePrefix> CoursePrefixObjList { get; set; }
public List<Course> CourseObjList { get; set; }
private GEApprovalDataModel _db;
//Constructor
public Listings()
{
_db = new GEApprovalDataModel();
}
//Generate a list of all courses associated with the prefix and place in ViewData model
public void listCourses(ViewDataDictionary viewData, int prefixID)
{
var test = _db.CoursePrefix.Include("Course").First(cp => cp.id == 1);
//Show total courses for this prefix
viewData.Model = test.Course.ToList();
viewData["prefix"] = test.Prefix;
viewData["courseCount"] = test.Course.Count;
int courseCount = test.Course.Count();//Test
}
}
}
And in my view, I now have the following line:
Inherits="System.Web.Mvc.ViewPage<List<GEApproval.Models.Listings>>"
I'm still a little confused because I still cannot access the properties of the Course object when listing them in my view, because I'm only inheriting directly from CoursePrefix. I'm not sure what I'm missing. Do I need to have a constructor for the composite object? Do I need the inherit and implementation statements for CoursePrefix and ICourse respectively if I'm already, supposedly, exposing the properties of each within the Listings wrapper class??
Create a ViewModel class that has properties that expose both sets of data. Bind to that instead.
You model can only contain one object. If you have multiple objects you need for your view you will have to create a composite object.
This can be as simple as exposing multiple properties that match the object types needed in the view.
public class ModelObj
{
public List<CoursePrefix> CoursePrefixObjList {get; set;}
public List<Course> CourseObjList {get; set;}
}
Then just use your ModelObj in the view
Inherits="System.Web.Mvc.ViewPage<ModelObj>"
This is not inheritance, it's generics, very differant.
No it isn't possible, you need to combine them into a wrapper class containing two references, or simply adding a reference to CoursePrefix within the Course class would seem reasonable, but i base that on a very very limited understanding of your model!
There is no such thing as multiple inheritance in .Net. As the other answers have mentioned, use a composite ViewModel object for this situation (this is generally considered a much better design choice, even in languages that support it).
If both of them are derived from a common type you could have your view be of that type. Then you may need to check the type in a few places and output the type specific properties after casting, while outputting all the common properties normally. The down side is that the View may be considered too smart in this case.
If these are really separate types, you should create two different Views, seems the more object oriented way to go.
You could create a composite View of these two types. I'd be against it, as one of them might always be null.
The real question is why you're trying to print out two different tables in the same View. If both of these tables are always filled then go for it and create the composite DTO, otherwise these seem like separate Views / one View of a common base.