MVC3: Binding models to dynamically created html elements - c#

I have a model which contains lists of other models, which also contains lists of other model types like the following for example:
public class Report
{
public string ReportId {get; set;}
public List<ReportOutput> ReportOutputs;
}
//output types = PDf, csv, txt, etc.
public class ReportOutput
{
public int OutputType {get; set;}
public List<DeliveryMethod> DeliveryMethods;
}
//delivery methods = email, ftp, etc.
public class DeliveryMethod
{
public string MethodName {get; set;}
}
I have a view that dynamically creates elements based on the contents of these objects using jQuery. The view allows users to check checkboxes to add or remove different outputs and within each output, different delivery methods. I need to know how to get the information back to the controller to load into a model to send back to the database. From reading around, it seems that I would just need to instantiate a model in the controller, then give each element in the view a name which corresponds to the model's properties and than call the controller function to retrieve all the data and continue the saving with the newly filled model, but my attempts at creating this controller function have failed.
So my question is, how would I create the controller function and do I just need to use the corresponding names on the html elements to allow a model to be created based on the information on the view?

If you follow the below pattern in generating name of form fields, the default model binder will take care of the rest else you have to do the model binding process yourself (tough job!).
ReportId,
ReportOutputs[0].OutputType,
ReportOutputs[0].DeliveryMethods[0].MethodName,
ReportOutputs[0].DeliveryMethods[1].MethodName,
ReportOutputs[1].OutputType,
ReportOutputs[1].DeliveryMethods[0].MethodName,

You should checkout the following article which covers those subjects and explains the naming conventions of your input fields that the default model binder expects so that it can bind them back when the form is submitted.

Related

Hiding/Showing properties and ordering properties in MVC

I am about to upgrade our software from ASP.NET WebForms to .NET MVC. All over the web it shows how to create a view based on a model, which is fine.
In this project the users can hide properties of the model to generate a view suitable for them, yet another client in another website and hide other properties.
The code is all the same, but i would like to know if there is a way to hide/show properties of a model based on a condition easily, hopefully without having a lot of IF statements all over my views.
Example - How can 1 client see only name and town, yet another client see all 3 properties. Just need to show based on a condition.
public class MyObject() {
public property name { get; set; };
public property town { get; set; };
public property customText { get; set; }
public MyObject() {}
}
NOTE: Users can also determine the order of these properties, can i do that as well easily ?
Just to say that creating separate views is not possible. The above is a very simple example of a model with properties. Our models can have about 100 properties, and the user can turn these on and off whenever they like, so it needs to be able to be done dynamically
Is there a way of creating a ViewModel on the fly?
Thanks in advance
Create a Property class or similar and model you data appropriately:
public class Property
{
public string Name {get;set;}
public bool Visible {get;set;}
public int Order {get;set;}
}
Then your view model can be similar to your example:
public class ViewModel
{
public Property Name {get;set;}
public Property Town {get;set;}
public Property CustomText {get;set;}
}
Well you cannot bind multiple models to your view.Obviously you have to do workaround in your view based on user roles.
Or else create a seperate model and view for each user roles.
I have been working on a project called Dynamic MVC.
http://dynamicmvc.com
It currently does not do what your asking. The functionality is already there, it is just not exposed the way you need it. However, if you are interested I will add the functionality so you can pass the properties you want to display in the querystring. Eventually, a customizable dynamic view will generate your page for you without any coding required. Also, the order of the properties would determine the order on your page. This would work for any model with the DynamicEntity attribute.
Let me know if your interested and I can include it in the next release.

How should I serialize dynamic form elements in MVC 5?

I have an MVC 5 project and part of that is a registration. Presently the form submits using the standard form submit action and my model binder does its job gloriously and all is good.
However, I now need to add a dynamic element to my form such that a user needs to be able to add on the fly invitations to add team members via a textbox for an email address and a dropdown list for that team members role... and an add another button to add another textbox and dropdown list for the next team member... and the next... ad infinitum...
Upon form submission, my model binder is going to fall over and not know what to do with these dynamic elements, so I need suggestions for how to get my model binder to deserialize these items into an array of email & role (role binds to an enum).
I've had it suggested that client side script can catch the submission and serialize the items to a text string and put that in a hidden form field that I can then deserialize manually on my controller action, which seems like it'll work okay, but it feels dirty.
What is the most straightforward way of achieving this? Is there anything built into .NET that will automatically handle this somehow if I configure my view correctly?
Your model can store your "dynamic" fields in a collection like:
public List<string> Emails { get; set; }
In your view you just create subsequent indexed names (exact naming depends on how you're generating this new fields):
model.Emails[0] // name="Emails[0]"
model.Emails[1] // name="Emails[1]"
In your controller action, there should be nothing special the model binder has to do. It will easily be able to handle the collections.
There is an old article from Phil Haack about binding to a List:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
I guess you could add a ICollection<Invitation>-Property to your model and set the Name of the form fields as explained in the article.
Although personally I would bind the data to a JavaScript-ViewModel and post JSON back to the Server using Ajax.
Upon clicking the "Add more" button, simply inject the textbox and dropdown and let the form serialize like usual.
On the server, accept a list of invitations:
public class Invitation
{
public string EmailAddresss {get; set;}
public int InvitationType {get; set; }
}
public ActionResult Register(MyCurrentModel dto, Invitation[] invitations)
{
//..
}

Passing complex type containing array of complex types to Web API

Extending on from this question, I'm trying to pass a complex object containing a collection of complex objects to an ASP.NET Web API controller action method, but I'm having trouble finding the correct format to use for my input object in the request. Continuing the example from Christopher Johnson's question (and removing some fields for simplicty), if I change his PhoneRequest object to contain a collection of phone numbers...
public class PhoneRequest
{
public string[] PhoneNumbers { get; set; }
public string State { get; set; }
}
...and I either pass the parameter as a URL encoded query string:
/api/phonenumber?id[0][State]=UT&id[0][PhoneNumbers][0]=555-1234567
...or POST it as a html form encoded (Content-Type: application/x-www-form-urlencoded) request body:
[0][State]=UT&[0][PhoneNumbers][0]=555-1234567
...then everything works great. But If I change PhoneNumbers from a collection of strings to a collection of a new PhoneNumber type...
public class PhoneRequest
{
public PhoneNumber[] PhoneNumbers { get; set; }
public string State { get; set; }
}
public class PhoneNumber
{
string AreaCode { get; set; }
string Number { get; set; }
}
...and I pass the object in what appears to me to be the logical way, given the previous result...
id[0][State]=UT&id[0][PhoneNumbers][0][AreaCode]=555&id[0][PhoneNumbers][0][Number]=1234567
...or again as a html form post body...
[0][State]=UT&[0][PhoneNumbers][0][AreaCode]=555&[0][PhoneNumbers][0][Number]=1234567
...then it still makes an attempt to bind it, and gets into my action method, but the model only contains State, and the phone number is this weird ComplexUriAndFormObject thing. Here's a copy-paste from my debugger Watch:
phoneRequest {ComplexUriAndFormObject.Models.PhoneRequest[1]}
[0] {ComplexUriAndFormObject.Models.PhoneRequest}
PhoneNumbers {ComplexUriAndFormObject.Models.PhoneNumber[1]}
[0] {ComplexUriAndFormObject.Models.PhoneNumber}
AreaCode null
Number null
State "UT"
Is there some way to specify this object correctly using this format? Or have I exceeded the limit of what ASP.NET Web API's built in model binders are able to do?
Note: Please don't say "just use POST" or "just use JSON/XML/Whatever as your Content-Type"... If I was able to do that, I would.
Until now, no built-in Web API model binders including derived FromUri and FromBody attributes support passing a complex object that contains any property of non-primitive class types (excluding the System.String) with query strings. I had the similar issues and have created a unique and advanced custom model binder, the FieldValueModelBinder class, to work on a target object hierarchy having generic list or array collections. I can use the pure query string type of source data without imbedding any JSON or XML structure into it. The model binder can be used as easy as the FromUri and FromBody attributes. It also works effectively for transferring query string data in both URI and request body.
Please read my article and download source code I have just posted using this link. You can also run the test app from the download source for your input string, model, and results. I hope that this is the right solution you need.

ViewModel with related objects

Relatively new to .Net MVC. Stumped by what appears to be a very simple problem.
I've got a few objects that are related to each other.
(as an example)
public class Asset{
public int Id{get;set;}
public AssetCategory AssetCategory {get;set;}
public string Value {get;set;}
}
public class AssetCategory{
public string Name{get;set;}
public DateTime SomeDate{get;set;}
public int Id{get;set;}
}
I want to create a new "Asset" object in my View and pre so I create an empty one with the AssetCategory set. Then pass it through as the model for that view.
I've tried having a #Html.HiddenFor(m=>m.AssetCategory)
Which obviously fails as it doesn't how how to convert from that object to a string and back.
If I have #Html.HiddenFor(m=>m.AssetCategory.Id) then my ModelState is valid, But doesn't have all the information tied to the AssetCategory.
In this situation, do I just have to get the correct versions of any detached objects from my DB?
As it stands, when I try to save my new Asset. I get an error because the non-nullable DateTime on the AssetCategory object is invalid.
If you only need the category information on the server, then yes, get that on the server and attach to your object before saving it.
You should only include the AssetCategory in your model if your client will change it, ie. you have a dropdown the user can choose from. In that case, add the id only and a list of valid items to your model. When your model is posted back, convert it to the object you need to save.
In other words, keep the classes you have to save to the db, but create a separate view model.
If all you need is the Id then your original option would work (but as you said no other details known based of posted back data only).
#Html.HiddenFor(m=>m.AssetCategory.Id)
If you want more information that that, try to make it modular by using an EditorTemplate.
#Html.EditorFor(m=>m.AssetCategory)
\Views\Assets\EditorTemplates\AssetCategory.cshtml
#model AssetCategory
#Html.HiddenFor(m=>m.Id)
#Html.DisplayFor(m=>m.Name)
That being said, you should be using ViewModels for this sort of thing not EntityModels. What I'd recommend is keep it as passing back only the Id, but then on your postback, load the full asset category info from the database using the AssetCategoryId prior to saving your Asset itself.
For performance EF doesn't load all the data into the model by default. So, you have to load this manually like this:
public ActionResult MyAction(int id)
{
var asset = db.Assets.Single(a => a.Id == Id).Include(a => a.AssetCategory);
return View(asset);
}
The Include method will load the related object into its model, so you will get all properties and #Html.HiddenFor(m=>m.AssetCategory.Id) will have the Id filled with the correct data.
Take a look at Loading Related Objects for more EF Related information!
Hope Its help you!

What approach should be used for model binding to an exisiting session object?

Here's my problem:
We have an intranet asp.net mvc 3 application with a controlled set of users. We have a Person class, that contains a large amount of information, that is initially loaded and stored in the session. The data/editing for this object spans across many screens. Basically, each screen is a subset of the Person's data.
I'm trying to take advantage of the built in model binding in asp.net mvc. Should I create a data class that binds the form data from each screen and then updates my session object using a service object?
Example below: DxFormData contains a subset of the person data and will only be used as a parameter on this method.
public ActionResult Dx(DxFormData data)
{
// Update current session Person object with data passed in if modelstate is valid
var viewModel = this.GetDxViewModel();
return View(viewModel);
}
public class DxForm Data
{
public string AdmitDx { get; set; }
public string PrinDx { get; set; }
}
I'm looking for thoughts on this approach and if there's a better solution available to me. The problem that I see, is that the person class contains all the data and I'm creating another class with a subset of that data. Obviously, duplicating the properties.
Side note: I did write a custom model binder that returned the session person for binding. However, I am continually getting errors when it attempts to bind.
I don't see problem with this approach. If you try to use the Parent class as the action parameter then in each form submit action then you will get validation errors because the model is not completely filled, so you should use view models in this case and unfortunately you can't avoid duplicating properties.

Categories