Post item of IEnumaration to controller - c#

I am having trouble figuring out what is the best aproach to a rather simple problem.
I have a payment view-model that contains a list of avalible payments and some base properties like title, content...
So in my view I use #using(Html.BeginForm()) and inside that I loop over the payments and render each out and of course the view-model that I recive in my controller post has and empty list of payment methods.
I can see that if I use a for and print out model.paymentMethods[i].Prop than it can map it but is this the right aproach or can I do something even smarter?

For the model-binder to work you need to use indexed access.
#foreach (var item in Model.PaymentMethods)
{
#Html.EditorFor(m => item.Prop);
}
does NOT work.
You need to use:
#for (int i = 0; i < m.PaymentMethods.Count(), ++i)
{
#Html.EditorFor(m => m.PaymentMethods[i].Prop);
}
Otherwise the model-binder can't map it back in the post-back.

Yoy can create a editor template of Payment type. And in view you can use that editor template like:
#Html.EditorFor(m => m.PaymentMethods)

Related

Using #TextBoxFor with #model IEnumerable<>

I have a primary controller/view that uses an #model IEnumerable<ReportModel> for the strongly typed view. I would like to use #TextBoxFor() but this does not work with the IEnumerable (or, at least, intellisense does not like it).
Is it necessary to create a partial view or something else to allow use of the strongly typed model in creating form elements?
You need to change the IEnumerable to be an IList and loop through the records of your collection and choose the properties that will appear in the textboxes:
#model IList<ReportModel>
#for(int i = 0; i < model.Count; i++)
{
#Html.TextBoxFor(m => model[i].Property)
}
Thanks to Stephen Muecke for the correction.

MVC View interacting directly with Model - good or bad?

I have a pretty basic web application showing the content of a table. The user can change some parameters by clicking on links.
To simplify let's consider only a simple one, the name of the column used to order rows.
User can either click on the column or select the name of the column from a right sidebar to change the ordering.
I use an <option> element to show the list of available column name.
Is it correct to take those names directly from the Model object or should I somehow pass them from the Controller? This is the code by now
<select>
#{
// get list of all properties names
List<string> EtlProceduresNames = (((new MIPortal.Models.EtlProcedure()).GetType().GetProperties()).Select(item => item.Name)).ToList();
// display an option item for each property name
foreach (string EtlProceduresName in EtlProceduresNames) {
<option value=#EtlProceduresName >#Html.DisplayName(EtlProceduresName)</option>
}
}
</select>
This way the View interact directly with Model. Is this a conceptual error? Should I put that code on the Controller and then store the name list on a ViewBag object?
You should not access the Model directly from the view as that is against the MVC pattern (separation of concern), and you also should NOT be putting this data into the view bag.
View bag should only be used for the simplest of things to pass around and not when you have objects full of data.
Look into using Viewmodels, that is the best way of coding asp.net MVC.
Example of such-
http://www.codeproject.com/Articles/826417/Advantages-of-ViewModel-in-MVC-Model-View-Controll
I can suggest you to use so called ViewBag injection.
You need to declare an action filter attribute class for your data.
public class EtlProceduresNamesAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//you can add some caching here
filterContext.Controller.ViewBag.EtlProcedures = (new MIPortal.Models.EtlProcedure()).GetType().GetProperties()).Select(item => item.Name)).ToList();
}
}
All you to annotate your view action with this attribute, for example
[EtlProceduresNames]
public ActionResult Action()
{
return View();
}
You need to replace you view code with
<select>
#foreach (string EtlProceduresName in ViewBag.EtlProceduresNames)
{
<option value=#EtlProceduresName >#Html.DisplayName(EtlProceduresName)</option>
}
</select>

Edit IEnumerable collection of items simultaneously in a View in MVC 5

I have a collection of items, name it type "A", that I want to view and edit some of its attributes in a View. I would like it to save simultaneously, however, this does not seem to work as it does not seem like it is passing anything back to the Post method.
My Controller:
[HttpPost]
public ActionResult inline(IEnumerable<A> listA)
{
for (int i = 0; i <= listA.Count(); i++ )
{
A theObj = listA.ElementAt(i);
db.SaveChanges();
} //Somehow this is returning to be Null
}
My View:
#model IEnumerable<A>
#using (Html.BeginForm())
{
#Html.EditorForModel("Multiple")
}
So far this prints out all the Id of the entries (with no formatting/line breaks, of course) for some reason.
And I am not sure how to create an editor template "Multiple", this is what I have so far:
#model Models.A
#Html.EditorFor(x => x.Id)
I'm not sure where to put it / create it, so I just made it as another view in the same folder.
Any pointers how I can make this to work, so that I can edit multiple entries of the same object in the same view and pass it back to the controller and save it? I'm a newbie to MVC, so if this seems like a really simple question, I apologize in advance. Thanks!
Like most things in programming, there's multiple ways to achieve this. If you want to go the editor template route, though, it's pretty straight-forward.
As #JamieD77 pointed out, editor templates go into Views\Shared\EditorTemplates. The most important part of that path is the EditorTemplates directory convention, though. Just as with any other view in MVC, you can override/fallback depending on where you place your view. For example, Areas\Foo\Views\Shared\EditorTemplates, will work as well, but then it's only available to the Foo area. Or, you can override it for a particular controller by placing it in Views\Foo\EditorTemplates.
Then, the name of the view should correspond with the type it's intended to be used with. In your case, the view should be named A.cshtml. You can technically specify the view name manually by either passing it to EditorFor or using something like UIHint, but it's easier and more foolproof to just rely on convention here.
Inside this view, you should create the look and feel you want to have a for a single instance of A, with all editable properties represented. Then, in your main view, you simply call Html.EditorFor on the collection property, which in your case here, is the whole model:
#Html.EditorFor(model => model)
Razor will realize it has a collection and render the editor template for each item in the collection. Importantly, because it has this context, it will also be able to generate the appropriate indexes on the field names.
If you did something like the following instead:
#foreach (var item in Model)
{
#Html.EditorFor(m => item)
}
Your field names would not be indexed, and the modelbinder would not know what to do with the posted data. If you wanted to use a loop, you would have to use indexing inside the loop:
#for (var i = 0; i < Model.Count(); i++)
{
#Html.EditorFor(m => Model[i])
}
That then gives Razor the proper context to generate appropriate field names. However, importantly, that approach requires utilizing a List<T> structure, rather than something like IEnumerable<T> or ICollection<T>.

ASP.Net MVC Razor Sum and Count functions

Is it possible to use "Sum" within Razor, so you can sum up what has been interated through on the view. ie. my view is like this:
#model IEnumerable<cb2.ViewModels.ResultsVM>
...
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Qualified)
</td>
...
}
I then want to sum up all of the Qualified in at the bottom of the screen similar to this:
#Model.Qualified.Sum()
But I get the error:
'System.Collections.Generic.IEnumerable<cb2.ViewModels.ResultsVM>' does not contain a definition for 'Qualified'
I thought it would have been easy in Qazor to simply use Sum or Count on a model?
thanks, Mark
I think you want:
#Model.Sum(i => i.Qualified)
Qualified is a property of the items within the model, not the model itself.
Remember that Model is an IEnumerable<cb2.ViewModels.ResultsVM>, it does not contain a property for Qualified, but each item within the collection does. So you can call Sum directly on the collection and specify the property that you want to sum, namely Qualified...
#Model.Sum(x => x.Qualified)

Model binding to a list EditorFor compilation error

What I'm trying to do here appears to be pretty common, but I can't run this code. I get a compilation error.
I'm trying to bind to an ienumerable or icollection in a viewmodel. Is my syntax wrong? Is there a new way of doing thing I missed.
#for(var i=0; i < Model.traces.Count(); ++i)
{
#Html.EditorFor(x => x.traces[i].status)
}
The details of my architecture are at a previous post of mine that led to this...
Getting error on POST with Entity Framework - Value cannot be null. Parameter name: source
Is my syntax wrong?
Yes, IEnumerable or ICollection do not have indexer: x.traces[i].
It would be better if you used a collection whose elements can be accessed by index such as IList<T> or T[], depending on the concrete type of your view model.
Then you will be able to do this:
#model IList<MyViewModel>
...
#for (var i = 0; i < Model.traces.Count; i++)
{
#Html.EditorFor(x => x.traces[i].status)
}
The framework is quite smart in this case, if you have made your own EditorTemplate for the model Trace, you can actually just write
#Html.EditorFor(m => m.traces)
and MVC will render an editor for each item in the list and give you the right fieldnames for each index.

Categories