using Variable property Name with Razor MVC - asp.net-mvc

I am trying to have all the models used for specific searches into one Generic search view.
I need to render only some of the fields in the model, either with something similar to this : (Psudo code)
foreach (string textBoxFieldName in TextBoxFieldNames)
{
Html.Toolbox_TextBoxFor(m => m.textBoxFieldName)
}
or having attributes on the model and checking when that attribute applies e.g.
in the model I'll have something like this :
[AppliedCases("Case1","Case4","Case77")]
[ControlToUse("TextBoxFor")]
public string LastName { get; set; }
And some how in the view will be able to go trough all the properties check that if the CurrentCase is one of the AppliedCases of the Model property and if so then use the Razor magic display it accordingly
My question is this correct approach and if so how do I implement it, looking for some examples with dynamic rendering (if that is the name of this topic)

You could use the overload of Html.Editor that takes the name (as string) of the property you want to render:
var fieldNames = new string[] {"LastName"};
foreach (string fieldName in fieldNames) {
#Html.Editor(fieldName)
}
You can use the UIHint attribute in the ViewModel to influence which editor template shall be used:
[UIHint("MySpecialStringEditor")]
public string LastName { get; set; }
At the shop where I work, we do not use this approach. Rather, we use different explicit Views of the same ViewModel. This allows for more flexibility and is easier to understand and maintain.

Related

How to make DropDownListFor use item IDs instead of indices?

I have a table with a dropdown list in each row, like this:
#Html.DropDownListFor(m => m.Transactions[i].CategoryID, ...)
and everything mostly works. I can select items, submit the form, and the model has my updated selections. So far so good.
The problem is that this isn't very reliable. Since the name of each dropdown is based on an index rather than an ID that means the match-up between the post values and the actual items living in the database are based on indices. Most of the time that works fine, but what if the list of items changes between the time the page loads and the time a user does a postback? The indices have changed, which means the post data won't match up correctly, and bad things happen. OR I've seen the browser incorrectly try to preserve selections in dropdowns between posts, but because the list of items is changing (what may be item #2 now may be item #3 by the time the page is refreshed) and everything is based on indices, the wrong dropdowns get the wrong values.
So basically, how can I force the dropdowns to generate a name and ID that looks more like this:
Transactions_CategoryID_12385652 // 12385652 is the CategoryID
rather than this:
Transactions_4_CategoryID // 4 is the array index
and still have the benefits of automatic binding?
Edit: The second issue I mentioned (input values being restored incorrectly after a refresh) seems to only happen with Firefox. https://bugzilla.mozilla.org/show_bug.cgi?id=46845
You'd have to write your own custom model binder as well as a Html extension to generate the element names in this way, or you could generate the markup manually using Razor.
Is there a particular reason you want need to do it this way? You're almost always following the conventions of the framework unless there's a good reason not to.
You can pass the exact collection you want to the view and bind that to the DropDownFor html helper.
Say you have a Person.
public class Person
{
public int Id { get; set; }
public int Name { get; set; }
}
And you want to add a new person. Create a view model. Create a property in this view model of type SelectList. This select list will hold the collection of the model you want to populate the dropdown list with.
public class PersonViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public SelectList MySelectList { get; set; }
}
Let's say you want the drop down list to hold the Person's Id as the selected value and the Person's name as the text value. You may want to create a second view model to represent that or use an anonymous object. Let's call the collection myCollection. And let's say that the collection is made up of objects that have two properties (and Id and Name property). Now simply pass this view model with a value for MySelectList.
var viewModel = new MyViewModel
{
MySelectList = new SelectList(myCollection, "Id", "Name")
};
In your razor view you can set up the #Html.DropDownListFor like so:
#Html.DropDownListFor(m => m.Id, Model.MySelectList)
In order to pass a select value to the view for the dropdownlist simply use the SelectList constructor overload that allows this value to be passed:
new SelectList(myCollection, "Id", "Name", selectedValue)

MVC 4 Client Side Field validation for a list of fields (instead of fields as members of a class)

I am a long time backend .Net developer who is new to web application development. I am using MVC 4, Razor, EF 5, and I have a basic understanding on how to make a routine DB driven MVC 4 site with these tools.
I need to create a custom form capability for a workflow scenario. I have the entire code-first schema designed and classes for different formfield types, and formvalue type.
The model that will be passed to a view will be a form class with a list of formvalues, which contain form field specifications. So the view will have to iterate through the form fields and dynamically choose what editors to use and so on.
My problem is that this prevents me from using any data annotation for client side validation. And every place I find advice on custom validation assumes (not surprisingly) that all the fields are members of a class. Whereas, in this case, all the fields are on a list.
My goal is to end up with validation messages on each field and a validation summary as you would normally have if the view was simply bound to an annotated class.
Any suggestions on how to approach this? Thanks in advance.
Imagine view logic that has the following:
#foreach (var fieldvalue in Model.FormFieldValues) {
// Logic that chooses the appropriate editor based on typeof(fieldvalue.FormField)
// Binds the editor to fieldvalue.Value
// Validation specs are contained in the Formfield obtained by fieldValue.FormField
// And my problem is setting up the validation without the aid of the normal method
// of data annotation or class level custom validation.
}
and the fieldvalue class looks like this:
public class FormFieldValue : EboEntity
{
public string Value { get; set; }
[Required]
public int FormFieldId { get; set; }
[Required]
public virtual FormField FormField { get; set; }
[Required]
public int FormId { get; set; }
[Required]
public virtual Form Form { get; set; }
}
And imagine that the FormField object has fields such as Required, Maxlength, and so on, depending on the kind of field it is (i. e. FormFieldText would be a subclass of FormField)
ANSWER:
Ok, you can tell I am new at MVC.
The answer to my question is that the specific editors take htmlAttributes which can control validation. So in the case where one of my formfields is a required textfield of stringlenth(10), I can invoke the editor as follows:
<div class="editor-field">
#Html.TextBoxFor(model => model.NoTexty, new {
required = true,
maxlength = 10,
placeholder = "Texty"})
#Html.ValidationMessageFor(model => model.NoTexty)
</div>
but in my case the htmlAddtributes won't be hard coded, but rather they will come from the fields in the formvalue.FormField object.
Sorry if I wasted anyone's time on this. But I will leave this here for any other newbies like me.

Custom Class in Razor View?

Within my Controller I have a class called "ObjectData" that contains an ID and a string:
public class ObjectData
{
public int ObjectId { get; set; }
public string Name { get; set; }
}
I'm trying to pass a List of these to the view via ViewBag, but I don't know how to loop through the items in the array since the classtype isn't normal. I'm doing it this way because I don't want to pass a bunch of Objects and their data to the view, when I only need the ID and Name (is this a valid concern?).
I'm thinking of looping through like this:
foreach (ObjectData i in ViewBag.ParentSetIds)
{
#Html.ActionLink(i.Name, "Detail", new { objectId = i.ObjectId }, null)
}
But Razor doesn't recognize that class type. How can this be accomplished?
You must fully qualify your typename on the line:
foreach (Put.Your.Namespaces.Here.ObjectData i in ViewBag.ParentSetIds)
Razor do not use the same using declaration as your controllers. You may use web.config in the View directory to add such namespaces not to fully qualify it everytime.
Regarding the question if you should be concerned about passing such objects to view. No, there is no need to worry about it. I suggest to move the object ObjectData from controller to the folder next to the controllers folder named ModelView or ViewModel and create the class here. This is something like publicly accepted "hack" to have models which represents just another view on some "real" model. It is same like when you generate MVC3 project it creates for you file AccountModels.cs which contains exactly the same kind of models. But you find it in Model folder, while it may be discussed if it should be rather in ViewModel folder. Also, pass this data as Model not as the part ViewBag if it is not really just helping data.
You could use:
foreach (var i in ViewBag.ParentSetIds)
And let the compiler determine the namespace based on the ViewBag.ParentSetIds
Within my Controller I have a class called "ObjectData" that contains
an ID and a string:
Wait, what? Why do you have a class in your controller?
I'm trying to pass a List of these to the view via ViewBag,
Just use a view model. If you are, you can make List<ObjectData> part of it. In your controller, you load up that list (lets call it ObjectDataList), and send it to your view.
In the view (razor), you'd have something like:
#model MyProject.MyModel
#foreach(var i in Model.ObjectDataList)
{
#Html.ActionLink(i.Name, "Detail", new { objectId = i.ObjectId }, null)
}
Edit:
For clarification, your view model could be:
public class MyModel
{
public string Title {get;set;}
public List<ObjectData> ObjectDataList {get;set;}
}

Returning specifically shaped POCOs to ASP.NET MVC actions

In my ASP.NET MVC project, my actions typically call a Service layer to get data. I use the same dozen or so POCOs for all my models. I also plan on using the Service layer in console applications and maybe expose a web api at some point.
To make my database operations more efficient, my service layer only hydrates the properties in the model that are relevant to the particular method (which at this point is mostly driven by the needs of my controller actions).
So for example I might have a class Order with properties Id, Name, Description, Amount, Items. For a given service call I might only need to populate Id, Name, Items. A consumer of that service won't necessarily know that Amount is 0 only because it didn't populate the property.
Similarly, the consumer won't know whether Items is empty b/c there actually aren't any items, or whether this particular service method just doesn't populate that property.
And for a third example, say one of my views displays an ItemCount. I don't want to fully populate my Items collection, I just need an additional property on my "model". I don't want to add this property to my POCO that other service methods will be using because it's not going to be populated anywhere else.
So the natural solution is to make a POCO designed specifically for that method with only those 3 properties. That way the consumer can know that all properties will be populated with its real values. The downside to this is that I'll end writing tons of similarly shaped models.
Any advice on which method works best?
You could use Nullable Types to indicate the missing properties with a null.
For example:
class Order {
public int Id {get;set;}
public string Name {get;set;}
public string Description {get;set;}
public decimal? Amount {get;set;}
public List<Item> Items {get;set;}
}
And then if Items == null, it wasn't set. If it's an empty new List<Item>(), it's set but empty. Same for Amount. If Amount.HasValue == false, it wasn't set. If Amount.Value is 0.0d, it's set and the item is free.
Why don't you use LINQ projection?
One service method does something like:
return DbContext.Orders.Select(o => new { Id = o.Id, Name = o.Name, Description = o.Description });
while the other service method does something like:
return DbContext.Orders.Select(o => o);
I'm not sure how your application is architected, but this may be a way around creating 100's of POCO's.
Hope this helps! Good luck.
You could pass in a selector Func that returns dynamic:
public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector) { ... }
I'm not sure how you are accessing data, but the following shows how this would work using a List<T>:
class Program
{
static void Main(string[] args)
{
var service = new Service();
var orderNames = service.GetOrders(o => new { o.Name });
foreach (var name in orderNames)
Console.WriteLine(name.Name);
Console.ReadLine();
}
}
public class Service
{
private List<Order> _orders = new List<Order>
{
new Order { Id = 1, Name = "foo", Description = "test order 1", Amount = 1.23m },
new Order { Id = 2, Name = "bar", Description = "test order 1", Amount = 3.45m },
new Order { Id = 3, Name = "baz", Description = "test order 1", Amount = 5.67m }
};
public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector)
{
return _orders.Select(selector);
}
}
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Amount { get; set; }
}
The use of nullable values is a good solution, however it has the downside you have no way to matk required fields. That is you cannot use a required attribute on any property. So if there is field that is obligatory in some views you have no way to represent it.
If you don't need required fileds validation this is ok. Otherwise, you need a way to represent which fileds are actually used, and then to write a custom validation provider.
A simple way to do this is to use a "Mask" class with the same property names of the original class, but with all fields boolean: a true values means the field is in use.
I used a similar solution in a system where the properties to be shown are configured in a configuration files...so it was the unique option for me since I had no possibility to represent all combination of properties. HOWEVER, I used the "Mask" class also in the View, so I was able to do all the job with just one View..with a lot of ifs.
Now if your 150 service methods and probably about 150 Views...are all different, then maybe it is simpler to use also several classes ...that is in the worst case 150 classes..the extra work to write them is negligible if compared to the effort of preparing 150 different Views.
However this doesnt mean you need 150 POCO classes. You might use an unique POCO class that is copied into an adequate class just into the presentation Layer. The advantage of this approach is that you can put different validation attributes on the various classes and you don't need to write a custom Validation provider.
Return the entire POCO with nullable types as mentioned by #sbolm. You can then create a ViewModel per MVC page view that receives a model with the specific properties it needs. This will take more performance (insignificant) and code, but it keeps your service layer clean, and keeps your views "dumb" in that they are only given what they need and have no direct relation to the service layer.
I.e. (example class from #sbolm)
class Order {
public int Id {get;set;}
public string Name {get;set;}
public string Description {get;set;}
public decimal? Amount {get;set;}
public List<Item> Items {get;set;}
}
// MVC View only needs to know the name and description, manually "map" the POCO properties into this view model and send it to the view
class OrderViewModel {
public string Name {get;set;}
public string Description {get;set;}
}
I would suggest that instead of modifying the models or creating wrapper models, you have to name the service methods such that they are self-explanatory and reveals the consumer what they returns.
The problem with the nullable approach is it makes the user to feel that the property is not required or mandatory and they try inserting instances of those types without setting those properties. Is it won't be bad having nullables every-where?
It won't be a good approach to change the domain models since all you want is just to populate some of the properties instead of that you create service with names and descriptions that are self-explanatory.
Take the Order class itself as the example, say one service method returns the Order with all the items and the other one returns only the details of the Order but not the items. Then obviously you may have to create two service methods GetOrderItems and GetOrderDetail, this sounds so simple, yes it is! but notice the service method names itself tells the client what it is going to return. In the GetOrderDetail you can return an empty items or null (but here I suggest a null) that doesn't matter much.
So for new cases you don't need to frequently change the models but all you got to do is add or remove the service methods and that's fine. Since you are creating a service you can create a strong documentation that says what method does what.
I would not performance optimize this to much unless you realy get performance problems.
I would only distinguish between returning a flat object and an object with a more complete object graph.
I would have methods returning flat objects called something like GetOrder, GetProduct.
If more complete object graphs are requested they would be called : GetOrderWithDetails.
Do you use the POCO classes for the typed views? If yes: try to make new classes that serve as dedicated ViewModels. These ViewModels would contain POCO classes. This will help you keeping the POCO classes clean.
To expand on the nullable idea, you could use the fluentvalidation library to still have validation on the types dependent on whether they are null or not. This would allow you to have a field be required as long as it was not null or any other validation scheme you can think of. Example from my own code as I had a similar requirement:
Imports FluentValidation
Public Class ParamViewModelValidator
Inherits AbstractValidator(Of ParamViewModel)
Public Sub New()
RuleFor(Function(x) x.TextBoxInput).NotEmpty.[When](Function(x) Not (IsNothing(x.TextBoxInput)))
RuleFor(Function(x) x.DropdownListInput).NotEmpty.[When](Function(x) Not (IsNothing(x.DropdownListInput)))
End Sub
End Class

Can you remove the HTML Field Prefix from strongly typed models in MVC 3?

I have a view model like this:
public class EditVM
{
public Media.Domain.Entities.Movie Movie { get; set; }
public IEnumerable<Genre> Genres { get; set; }
}
Movie is the real entity I wish to edit. Genres is simply present to populate a drop down. I would prefer that when I call:
#Html.TextBoxFor(m => m.Movie.Title)
inside my strongly typed view that the input control have a name = "Title" instead of "Movie.Title"
I do not wish to split my view into partial views or lose my strongly typed view by using ViewData or the like.
Is there a way to express to the View that I do not wish to have the Movie. prefix? I noticed that you can set:
ViewData.TemplateInfo.HtmlFieldPrefix = "x";
in the controller, but unfortunately it seems only to allow adding an additional prefix. Setting it to "" does nothing.
Is there any work around for this? Or am I stuck with the unfortunate prefix that isn't really necessary in this case if I wish to keep strongly typed views and lambdas?
Thanks for any help.
Update:
Here's the controller actions to maybe make things a bit clearer.
public ActionResult Edit(int? id)
{
var vm = new EditVM
{
Movie = id.HasValue ? _movieSvc.Find(id.Value) : new Movie(),
Genres = AppData.ListGenres()
};
return View(vm);
}
[HttpPost]
public void Edit([Bind(Prefix = "Movie")]Movie m)
{
_movieSvc.AddOrUpdateMovie(m); //Exceptions handled elsewhere
}
No, in order to do what you want you would have to rewrite the Html helpers, and then you would have to write your own model binder. Seems like a lot of work for minimal gain.
The only choice is a Partial view in which you pass the Movie object as the model. However, this would require you to write your own model binder to have it be recognized.
The reason you have to do m.Movie.Title is so that the ID has the correct name, so the model binder can recognize it as a member of your model.
Based on your update:
Your options are:
Use non-strongly typed helpers.
Use a partial view.
Rewrite the stronly typed helpers
Don't use the helpers at all, and write the values to the HTML
Personally, i'd just use 1 or 2, probably 2.
EDIT:
Based on your update above. Change your code to this (note, Genres does not get posted back to the server, so m.Genres will just be null on postback):
[HttpPost]
public void Edit(EditVM m)
{
_movieSvc.AddOrUpdateMovie(m.Movie); //Exceptions handled elsewhere
}
EDIT:
I did just think of an alternative to this. You could simply do this:
#{ var Movie = Model.Movie; }
#Html.TextBoxFor(m => Movie.Title)
However, if there was a validation error, you would have to recreate your EditVM.
I have a view model like this
I think that you might have some misunderstanding about what a view model is. A view model shouldn't contain any reference to your domain models which is what those Movie and Genre classes seem to be. I mean creating a new class that you suffix with VM and in which you stuff all your domain models as properties is not really a view model. A view model is a class that is specifically designed to meet the requirements of your view.
A much more correct view model would looks like this:
public class EditVM
{
public string MovieTitle { get; set; }
public IEnumerable<GenreViewModel> Genres { get; set; }
}
and in your view you would have:
#Html.EditorFor(x => x.MovieTitle)
#Html.EditorFor(x => x.Genres)
Another option is to either use the TextBox(string name, object value) overload instead of the TextBoxFor:
#Html.TextBox("Title", Model.Movie.Title)
You could also specify the input tag HTML instead of using a helper.
Another option is to take EditVM as your postback parameter. This is what I would do. My post action parameter is always the same type of the .cshtml model. Yes there will be properties like lists that are null, but you just ignore those. It also allows you to gracefully handle post errors as well because if there is an error you'll need to return an instance of that view model anyhow, and have the values they submitted included. I usually have private methods or DB layer that handles retrieving the various lists that go into the ViewModel, since those will be empty on postback and will need to be repopulated, while not touching the properties that were in the post.
With your post method as it is now, if you need to return the same view, you've gotta create a new EditVM and then copy any posted values into it, and still populate the lists. With my method, you eliminate one of those mapping steps. If you are posting more than one thing, are you going to have umpteen different parameters on your post action? Just let them all come naturally into a single parameter typed to the EditVM of the View. While maybe having those null properties in the VM during the postback feels icky, you get a nice predictable consistency between View and postback IMO. You don't have to spend alot of time thinking about what combination of parameters on your post method will get you all the pieces of data from the form.

Resources