Posting multiple values using MVC - asp.net-mvc

I have a model with a property that points to a file that contains HTML. My strongly typed view to this model uses a custom HTML helper method to resolve and return the HTML from the file. Works great so far.
The HTML read from each file will contain various controls whose values I need to retrieve when the form is POSTed.
What would be the best way to have access to the POSTed control values in my controller method?
I would prefer a non jQuery solution, but I am not sure if the MVC framework can provide these values to me? Can it provide a list of key/value pairs to the controller somehow?

You could use the FormCollection in ASP.NET MVC.
public ActionResult SomeAction(FormCollection form) {
...
}

You have essentially two options.
1) Use the old fashioned Request variables as all we have done in ASP.NET web forms.
For example in your controller action method you can retrieve any value present on the form with the following method
public ActionResult SomeAction() {
var request = this.ControllerContext.HttpContext.Request;
bool boolParam = bool.Parse( request["boolParam"] ?? "false" );
}
2) Create a custom Model Binder to let the framework pack those values in a custom class object.
This method would be a little bit more difficult at the beginning because you have to create a custom Model Binder but it favour readability on your controller code. For further details on creating custom model binders give a look at the following links (you can find more with a simple search)
Custom Model Binder for Complex composite objects
Custom Model Binder and More UI Validation in ASP.NET MVC
A Custom ASP.NET MVC Model Binder for Repositories
Hope it helps

Is the content of the HTML files dynamic or known at design time? If you know it now, you could have each one post to it's own action and then strongly type the parameters.

Related

MVC4 Action method AutoMap actionfilter fails to convert the view model to domain model

so, I've seen this working on a previous project in MVC3, so wondering if a) i've screwed it up, or b) MVC4 is doing something different (can't see it would be).
I have a model bound Razor view which submits to a controller action method (as is the way with MVC)
post action method:
[HttpPost]
[AutoMap(typeof(MyViewModel), typeof(MyDomainObj))]
public void PostAction(MyDomainObj obj)
{... etc.. etc..
The action filter eventually does something like this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = filterContext.Controller.ViewData.Model;
NOTE: In Jimmy Bogard's example he used OnActionExecuted which I also tried..
The key issue I'm having is that at the pint where we get the "model" variable from the context, it's null. If I look at the filterContext.ActionParameters whilst debugging I can see a MyDomainObj instance!! which appears to (because it happens to have a prop name in common with the MyViewModel type) have been mapped from my form data!
So.. yes if I had MyViewModel type as the parameter to this method, then the param would be properly populated from the submitted form. But. I don't want to do that, I want to (and have done before based on JB's succinct how-to) converted the view model to domain model as part of the action executed/ing and then been able to just hit save on my domain model.
Summary - why is my ViewData.Model null on the post action filter?
Jimmmy also has/had a couple of ideas on how to implement post actions, along with another guy Matt Honeycutt who shares his view on how to do posts. I also believe Jimmy has gone in the direction of using explicit mapping in his get requests instead of attributes, as it can be hard to inject any extra code you need after mapping.
http://lostechies.com/jimmybogard/2011/06/22/cleaning-up-posts-in-asp-net-mvc/
http://trycatchfail.com/blog/post/Cleaning-up-POSTs-in-ASPNET-MVC-the-Fail-Tracker-Way.aspx
For a post you really want a couple of things imo, the original Entity and the Form Data. You could load the entity like you do for the GET request and do normal model binding to get the form data (remember you can accept a different model for post backs than you spit out in your view) then make the changes in the action.
Of course this would require using AutoMapper in your action, which you seem to be trying to avoid. But unless you write a custom model binder then you're not going to magically get the formdata in a model as the built in one looks at the action paramters to figure out what to bind. For this reason i'd recommend not using a domain model as a parameter for an action, as it may fill out fields that you don't wish it to.
I've also seen Jimmy using a ModelBinder to do something similar to your AutoMapGet, which may be another alternative method, but I'm guessing you've seen that post.
My standard post takes Matt's approach, moving the validation out into a global attribute so it can't be forgotten (there are some downsides to this) then accepting a new view model explicity for the form data, then I explicity load the entity, use automapper to update it and call save. Most the actions only end up being about 5 lines long.
I do avoid using the static methods on AutoMapper and pass a IMapper in via constructor injection. This allows me to make it a bit more testable if I really need to, however the methods are normally so simple the tests don't add all that much value.

Best way to add filter to URL in .NET MVC

I'll try to make this as concise as possible.
Webpage contains a table that allows for filtering and sorting
Changes to filtering and sorting should be reflected in the URL so the user can bookmark or share filtered views.
The question is: What is an effective convention of allowing all of the sort and filter syntax to be part of the URL and easily interpret/use it on the server without having to write a bunch of custom code that interprets it?
I've been doing some research and I came across the OData URI conventions and I like the way they do things.
http://www.odata.org/developers/protocols/uri-conventions
More research shows the the MVC 4 Web API allows for use of that convention by returning an IQueryable. This looks fantastic except for one part... I'm not implementing a RESTful API at this point and that's all it seems to work with. So how can I use something like OData and still return a View or PartialView? Is there something that will parse the OData URI convention into a C# object?
If anyone has any insights into this problem or suggestions, I'm all ears.
As for the url convention part of your question, I think you have answered your own question with OData. As for getting this data into a C# object I would use the following approach:
Use an action filter to interperet the url parameters and parse them into a c# object.
In your action filter add the url parameters to the route data and the c# object will be available in your action.
ASP.NET MVC Pass object from Custom Action Filter to Action
Take a look at the Telerik MVC grid, they use a GridAction action filter that does pretty much what you are asking.
I would look at custom model binding. A good overview can be found here: http://weblogs.asp.net/nmarun/archive/2010/02/25/asp-net-mvc-model-binding.aspx
It's typically used for POST requests with forms but there's no reason why you can't use it for GET requests too.
Basically, your approach should be to:
Create a new Model class with your filter/sorting parameters as properties:
public class TableParameters {
public string TableFilter { get; set; }
}
In your Controller's Action, add the model as a parameter
public ActionResult TableAction(TableParameters parameters) { /* Action logic */ }
Set your parameters in the URL by saying:
/Controller/TableAction?TableFilter=[your-filter-string]
The parameters object in your action will have the property populated with the value from the query string.

Asp.Net MVC Custom ModelBinder

I would like to bind a complex object to a view.
I create a custom ModelBinder inherited from DefaultModelBinder so if I post the form I can reach proper data in Controller.
But if I want to send data to the view the data will not be binded.
Which method of DefaultModelBinder should I override?
Or anybody knows the solution?
l.
The model binder is not used in the view. It is only used to bind request values to action parameters. HTML helpers are used in the view to generate input fields. So it is those html helpers that might need to be customized to show the proper data from your model.

ASP.NET MVC generic view for displaying data

I am working on a project involves exposing many data entities (more than 200) through an ASP.NET MVC application.
I do not want to write views for every entity mode and I am wondering if there is a way of having one generic view to display different models (for example, a view which reads the model properties(metadata) and generates a HTML table to display a list of entities.
If you only need to display, you can write your own generic renderer in few minutes using reflection. Or, you can use ModelVisualizer from MvcContrib, or Grid from MvcContrib which can do Html.Grid(Model).AutoGenerateColumns() to automatically show properties as columns. It doesn't support DataAnnotations or attributes, but you can write your own extension method that will use reflection to generate Grid's columns based on some attributes. And you can quickly turn this grid into jqGrid using jqGrid's tableToGrid() method.
If you need input support, there're several choices:
MvcContrib's InputBuilder (here you can see a simple example on how it's done with reflection)
MVC v2 InputFor(), I think it supports rendering several properties, but I'm not sure.
My solution does read metadata (attributes) from view models and generates jqGrid formatting and editing options automatically.
There should be commercial tools, too.
Two choices:
Dynamic Data Project
Have you tried Asp.net Dynamic data project that can automagically create what you need?
Asp.net MVC
But if you want to do what you're asking, you can always create a single view that will not have a strong type model. You will always pass data to it ViewData dictionary. The view would then parse that data and display what's required.
Routing
You can always create these two routes:
routes.MapRoute(
"EntityRoute",
"{entityName}",
new {
controller = "Entity",
action = "Display",
entityName = "SomeDefaultEntity" }
);
routes.MapRoute(
"EntityRoute",
"{entityName}/{recordId}",
new {
controller = "Entity",
action = "Details",
entityName = "SomeDefaultEntity",
recordId = string.Empty }
);
that will redirect all requests to the same controller action that will provide correct entity set data and pass it into the ViewData dictionary and return the view that will consume it. The second one will display details of a certain record within some entity (in case you need to provide master/details functionality).
MVC Contrib has Model Visualizer which let you display a Model by reflecting its properties. That a no go if you need any performance at all, but maybe it gets you started.

Passing model data to asp.net mvc EditorTemplates

ScottGu in this post link text shows how one can utilize EditorTemplates for things such as a Country DropDownList. My question is how can one pass a dynamic list of Countries to the EditorTemplate?
Even better you make the partial view strongly typed and pass the model to the EditorFor helper
#Html.EditorFor(m=>m.SelectedCountry, Model.AvailableCountries)
Probably the most elegant solution is using a Custom Attribute, you can later access Model metadata using: ViewData.ModelMetadata.
e.g:
[Foreign(Type="DropDown", TableName="Countries")]
public int IdCountry { get; set; }
where ForeignAttribute is a class you must declare, and later use it to build your editor template.
You can pass it in ViewData and feed ViewData from and ActionFilter if the data is required very often (although arguable it is an anti-pattern).
Similar to #Benja's answer
You can also use the [AdditionaMetaData(key,value)] attribute in a similar fashion without having to define your own attribute. Key and value have to be strings.
The extra data can be retrieved in the view with: #ViewData.ModelMetadata.AdditionalValues["DropDownData"]

Resources