Enumerable objects in ViewBag in MVC4 - asp.net-mvc-4

I have the below model say F:
public partial class F
{
[Key, Display(Name = "Id")]
public int FId { get; set; }
public int RId { get; set; }
public int FTId { get; set; }
public string C { get; set; }
public string U { get; set; }
public string D { get; set; }
[ScaffoldColumn(false)]
public System.DateTimeOffset Created { get; set; }
}
In the controller I have to read all the records of 'F' from database and assign those to an enumerable list of records.
For ex:
ViewBag.Cs = enumerable C column items (textbox)
ViewBag.Us= enumerable U column items (textbox)
ViewBag.FTIDs = enumerable FTId column items (this has to be a dropdown)
In my I have to show
#Html.Textbox(Cs);
#Html.Dropdown(FTIDs);
I gave only textbox and dropdows as an example, there could be many other controls like datetimes, checkboxes etc.,
I should be able to written each column as list a in viewbag and show it in MVC View.
Can somebody advise if this achievable and how?
Many thanks...

Don't use a viewbag for something like this - instead strongly bind your view to a model. Only use a view bag if you have something really small to pass.. anything complex you should always use a strongly typed view model, you get intellisence and its must cleaner for unit testing
View Model:
Public class MyViewModel
{
public List<F> MyListOfFObjects { get; set; }
}
Now when you create your view you can bind it to this view model in the popup or if you don't want to recreate it simply add a reference to it at the top of your view like so:
#model <your project>.<folder view model is in>.<view model name>
for example
#model AdventureWorks.Models.EmployeeViewModel.
In your controller you simply create this view model and pass it to your view such as:
public ActionResult Index()
{
MyViewModel vm = new MyViewModel();
// Initialize your view model
// Get all the F objects from the database and populate the list
return View(vm); // now your view will have the view model
}
Now in the view you can iterate through this view model
#foreach(var fObject in Model)
{
#Html.TextBoxFor(m => m.fId)
#Html.TextBoxFor(m => m.rID)
}
Here is a link to a list of the different #Html helpers that you can use btw
http://qkview.com/techbrij/aspnet-mvc-4
Reference for strongly binded views:
http://www.asp.net/mvc/tutorials/views/dynamic-v-strongly-typed-views

Related

Null viewmodel passed to controller

I have a basic 'create' view scaffolded from a domain model, so it is typed to the model
#model TblProduct
<form asp-controller="Product" asp-action="Create">
...
<input asp-for="Artist" class="form-control" />
...
I'm trying to add functionality and use a view model instead, and I'm starting with a very basic viewmodel with only that domain model within it:
public class ProductViewModel
{
public TblProduct P { get; set; }
}
Now I've changed the 'create' view to use the view model instead
#model ProductViewModel
<form asp-controller="Product" asp-action="Create">
...
<input asp-for="P.Artist" class="form-control" />
...
So I expect the model to be valid given that (aside from editing the variable names) I'm populating all the same fields from the form, and effectively no other fields have been added to the model.
An error occurs when I post the form, I pass a ProductViewModel parameter to the create method but on inspection it is null. However ModelState.IsValid is true. So the code tries to write to the db and fails.
public async Task<IActionResult> Create([Bind("ID,Artist,ProductTitle... (long list removed)...] ProductViewModel productAndItems)
{
var prod = productAndItems.P;
if (ModelState.IsValid)
{
_context.Add(prod);
...FAIL
Any idea what I should be checking here - what am I missing?
How do I get the view (typed to a viewmodel) to pass the model data to the controller? And if it's null, how can ModelState.IsValid be true? In the example above I have debugged, the parameter passed in productAndItems is null.
Your current Bind attribute is looking for the following properties ID,Artist,ProductTitle... (White-list) and it's not finding them so therefore it's ignore everything and treating it is as a (Black-list) item.
You can either decorate your ProductViewModel with the Bind attribute as follows:
[Bind(Include = "P")]
public class ProductViewModel
{
public TblProduct P { get; set; }
}
This will of course mean all the properties in the TblProduct will be bound when submitting
If you do not want all of the properties to be bound on submit for TblProduct then you can decorate the TblProduct with the Bind attribute as follows
public class ProductViewModel
{
public TblProduct P { get; set; }
}
[Bind(Include = "ID,Artist,ProductTitle")]
public class TblProduct
{
public int ID { get; set; }
public string Artist { get; set; }
public string ProductTitle { get; set; }
public string ProductSubTitle { get; set; } //we will not include this in our (White-list)
//more props
}
More reading at MSDN
you need to add a name attribute to your form so the controller will pick it up.

Passing Model via dropDownListFor() from view to controller

I am trying to pass a Model from my view to my controller using dropDownListFor.
After choosing something from the list it sends the model to my controller but it's content NULL.
This is what i have for the Model
public class Model
{
public int ModelId { get; set; }
[Required]
public string Name { get; set; }
}
This is what my ViewModel looks like
public class ModelVM
{
public List<Model> Models;
public Model SelectModel { get; set; }
public IEnumerable<SelectListItem> ModelItems
{
get { return new SelectList(Models, "ModelId", "Name"); }
}
}
The controller when i put data in the ViewModel looks like this
public ActionResult Index()
{
ModelVM modelVM= new ModelVM()
{
Models = manager.GetAllModels().ToList()
};
return View(modelVM);
}
Finally this is what i have in the View for the dropDownList
#using (Html.BeginForm("Home", "Home", FormMethod.Post))
{
#Html.DropDownListFor(model => model.SelectModel, Model.ModelItems)
<input type="submit" value="Go" />
}
So this is supposed to send Model to my controller.
But when i check the content of the passed Model in the controller, everything is NULL which isn't supposed to be because when i debug the view and check for the content of ModelItems, everything in the ModelItems is there.
Here is when i check the content of the passed Model
public ActionResult Home(Model model) <<<<<<<<<< Content == NULL
{
return View();
}
A <select> element on posts back a single value. Html has no concept of what your c# Model class is so you cannot bind to a complex object. You need to bind to the ModelId property of Model. Your view model should be
public class ModelVM
{
[Display(Name = "Model")]
public int SelectedModel { get; set; }
public SelectList ModelItems { get; set ;}
}
and in the controller
public ActionResult Index()
{
ModelVM modelVM = new ModelVM()
{
ModelItems = new SelectList(manager.GetAllModels(), "ModelId", "Name")
// SelectedModel = ? if you want to preselect an item
};
return View(modelVM);
}
and in the view
#Html.LabelFor(m => m.SelectedModel)
#Html.DropDownListFor(m => m.SelectedModel, Model.ModelItems)
and in the post method your model will be correctly bound with the ID of the selected model
Trying passing in type ModelVM to the Home action as that is the type being passed to Index view.
Instead of model.SelectModel in the drop-down declaration, you only need an int ID to capture which ID is selected.

How to use one model property in another view model?

I have 2 model classes contactdetails.cs and empmodel.cs
I have a view called empdetails.cshtml and the model i am using for this page is empmodel.cs.
I want to use a property Mobile from contactdetails.cs
Can I use like this in mvc?
#Html.TextBoxFor(m=>m.contactdetails.Mobile)
No. Since you are using empmodel for this view you cannot do it.
What you can you do is create a ViewModel with with contactdetails and empmodel and use that ViewModel in your view.
e.g.
public class EmpViewModel
{
public EmpModel Empmodel { get; set; }
public ContactDetails contactdetails { get; set; }
}
And in your view empdetails.cshtml you can use it as follow;
#model EmpViewModel
#Html.TextBoxFor(m => m.contactdetails.Mobile)

asp.net - MVC model binding to nested collection

I have a class:
public class ClientInformation{
public string UserName {get; set;}
public ICollection<RegionDistrictCity> RegionDistrictCity
{
get;
set;
}
public class RegionDistrictCity
{
public string Region { get; set; }
public string District { get; set; }
public string City { get; set; }
}
}
How should be formated the name attribute of input elements for properties Region, Distirct, City in html in order to make model binder populate collection "ICollection RegionDistrictCity"?
I tried to have an action method with parameter of type "ClientInformation" and html name attributes formated like "[index].PropertyName" but in that case only the property "UserName" is binded.
I tried to have action method with parameter name "client" and have html names attributes formated like "client[index].PropertyName" but it doesn't work. (in tha case if I there is a "List client" then it would get populated)
Thanks.
In MVC4 you should use a for loop instead of a foreach to bind your collection. Then the model binder will be able to populate your model when you submit your data.
#for (int i = 0; i < Model.RegionDistrictCity.Count; i++)
{
#Html.EditorFor(model => Model.RegionDistrictCity[i].Region)
}
But this will only work if you are not deleting or adding items to your collection dynamically.
If you want to do that, you should use the BeginCollectionItem helper created by steve sanderson. http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/

Trouble loading a child model in MVC 4

I have an OrderViewModel that includes an instance of a child _DetailsViewModel. The OrderViewModel has order header information and the _DetailsViewModel holds order detail information. Despite being separate Models, both have the same single data source..the Orders table. The reason that the details are in their own View Model is that those fields are reused on a different View, in the same visual arrangement, so I'm putting them in a Partial View to be reused as needed. Here is an idea of my main and child View Models:
public class OrderViewModel
{
public string OrderNum { get; set; }
public string CustonerName{ get; set; }
public double SalesPrice{ get; set; }
public _Details Details { get; set; }
}
public class _DetailsViewModel
{
public string PhoneNum { get; set; }
public string ItemNum { get; set; }
public double Quantity { get; set; }
public string PayMethod{ get; set; }
public string Comments { get; set; }
}
Within my controller I call a service that returns all data from the Orders table and returns a List of Order Entities.
orderService = new OrderService();
var orders = orderService.GetOrderInfo(StoreNum);
From there I use Omu.ValueInjecter to inject the results into the main View Model.
var orderViewModel = orders
.Select(x => new
OrderViewModel().InjectFrom(x)).Cast<OrderViewModel>()
.ToList();
return View(orderViewModel);
I need to also populate the _Details model so that I can pass it to the Partial View from within my main Order View...like below:
#Html.Partial("_OrderDetails", Model._Details)
Is there a way to populate the _Details Model from the single service call that is already populating the main Order Model? Will I have to add the _Details properties to the main Order View and then iterate the Order view to set each field of the corresponding _Details Model manually? Surely I'm missing something.
Thanks...
Move the entities out of your database first, in that manner you only issue one query request:
// apply any filter(s) needed here.
var orderList = orders.ToList();
// then do injecting using the "cached" orders
var orderViewModel = orderList
.Select(x => new OrderViewModel().InjectFrom(x))
.Cast<OrderViewModel>()
.ToList();
// then inject into your details model
var detailsModel = orderList
.Select(x => new _DetailsViewModel().InjectFrom(x))
.Cast<_DetailsViewModel>()
.ToList();
And a small suggestion if I may, remove the underscore for _DetailsViewModel to make the naming standard.
UPDATE:
How do I add the detailsModel to the orderViewModel afterwards to pass
to the Order View?
You just simply set it to the instance of OrderViewModel like so:
orderViewModel.Details = detailsModel ;
Then return orderViewModel to your view and do your thing there:
#Html.Partial("_OrderDetails", Model.Details)