Post IEnumerable collections from view in mvc - c#

I am developing an ASP.NET MVC application, populating dropdownlist to view working fine in get method. I am populating to view like this in my controller
[HttpGet]
public ActionResult CreateEmployeeFamilyDetails(EmployeeSuperClass employeeSuperClass, int i = 0)
{
employeeSuperClass.FamilyDetailsFields = new FamilyList();
employeeSuperClass.FamilyDetailsFields.familyMembersList.Insert(0, new EmployeeFamilyTable());
*employeeSuperClass.FamilyDetailsFields.employee_RelationTable = dt.GetRelations();*
*employeeSuperClass.FamilyDetailsFields.employee_BloodGroupTable = dt.GetBloodGroups();*
*employeeSuperClass.FamilyDetailsFields.employee_NationalityTable = dt.GetNationalities();*
return View("CreateEmployeeFamilyDetails", employeeSuperClass);
}
Please look into starred lines
In case if there is any errors in model am getting null reference
In post action method look like this
[HttpPost]
public ActionResult CreateEmployeeFamilyDetails(EmployeeSuperClass employeeSuperClass, string Command)
{
if (ModelState.IsValid)
{
return("some view");
}
else
{
return view(employeeSuperClass);
}
}
I know again we have to create instance to populate dropdownlist this is rubbish to do same again and again
Can anyone explain how to store dropdown list collection in view separately and post them also with model?
(note: employee_relationTable is IEnumerable collection and it is a relationtable type this table contains relation id and relationname fields and am using this table in this class like below
public IEnumerable<EmployeeRelationTable> employee_RelationTable { get; set; }
For rest also am using same approach
Can we post employee_RelationTable from view and how?
Please help and your help would be greatly appreciated

Assuming dt is a member field available to all methods, what you can do is DRY up the population of the DropDowns in the View Model in a separate method, e.g.
private void PopulateDropDownsOnViewModel(EmployeeSuperClass model)
{
model.FamilyDetailsFields = new FamilyList
{
employee_RelationTable = dt.GetRelations(),
employee_BloodGroupTable = dt.GetBloodGroups(),
employee_NationalityTable = dt.GetNationalities()
}
}
Which can be used in the Get:
[HttpGet]
public ActionResult CreateEmployeeFamilyDetails(EmployeeSuperClass employeeSuperClass, int i = 0)
{
PopulateDropDownsOnViewModel(employeeSuperClass);
return View("CreateEmployeeFamilyDetails", employeeSuperClass);
}
and in the Post (and any other controller actions which need the drop downs)
[HttpPost]
public ActionResult CreateEmployeeFamilyDetails(EmployeeSuperClass employeeSuperClass, string Command)
{
if (ModelState.IsValid)
{
return("some view");
}
else
{
PopulateDropDownsOnViewModel(employeeSuperClass);
return View(employeeSuperClass);
}
}
If the drop downs are static, you can also look at caching these to prevent wasted IO to the database.
But no, don't serialize the the data in the View somehow (remember WebForms ViewState?) or fetch data from the View directly - this violates the MVC paradigm - the controller is responsible for providing data for the View to render.

Related

About multiple models in one view, again

I'm trying to create a one view where I'll could edit data from 2 different model. I read this manual: Multiple models in a view and some other topics on SO, and I successful got data from 2 models in my view, but I can't understand why I can't edit it in my view.
So, in my view I have smth like:
#model Tuple<GroupProjectsModel,InfrastructureModel>
#Html.EditorFor(m => m.Item1.Data)
#Html.EditorFor(m => m.Item2.Data)
Then, when I'm trying to save data, some mistakes happened. I have following code in controller:
[HttpPost]
[InitializeEditPageAttribute]
public ActionResult Edit(GroupProjectsModel get, InfrastructureModel getInf)
{
if (ModelState.IsValid)
{
....
return ReturnView(get.Id, NameModule);
}
var tuple = new Tuple<GroupProjectsModel, InfrastructureModel>(get, getInf);
return View(tuple);
}
In this case variable ModelState.IsValid is equal false anyway. But I'm trying another case:
[HttpPost]
[InitializeEditPageAttribute]
public ActionResult Edit(Tuple<GroupProjectsModel, InfrastructureModel> tupleFromModel)
{
if (ModelState.IsValid)
{
...
return ReturnView(tupleFromModel.Item1.Id, NameModule);
}
var tuple = new Tuple<GroupProjectsModel, InfrastructureModel>(tupleFromModel.Item1, tupleFromModel.Item2);
return View(tuple);
}
and got mistake too. I have no idea what to do.
Just created another class that holds both classes.
public class EditViewModel
{
public GroupProjectsModel groupProjectsModel {get; set;}
public InfrastructureModel infrastructureModel {get; set;}
}

Persisting Dropdown information when ModelState is not valid

I'm having some issues with my DropDownLists, because when I post the information and my Model is not valid it comes back "empty" to the page triggering an error exactly like this question.
I've used the solution proposed there and it fixed my problem. Anyway, I wanted to avoid querying the database every time my ModelState is not valid and I came with this approach. I would like to know if it is valid or if there are better ways to do it now, considering that instead of MVC2 (which was the MVC version from the question) I'm now using MVC 5, maybe they added something new to tackle this.
What I've done was to use the TempData to persist the information when my model is not valid.
public class ViewModel
{
[DisplayName("Project")]
public int ProjectID { get; set; }
public List<SelectListItem> Projects { get; set; }
//Other fields
}
Now my Create() Action (that populates Projects)
[HttpGet]
public ActionResult Create()
{
ViewModel vmodel = new ViewModel();
vmodel.Projects = db.GetProjects(User.Identity.Name).Select(x => new SelectListItem { Text = x.Description, Value = x.Id }).ToList();
TempData["Projects"] = vmodel.Projects;
return View(vmodel);
}
And my post would be like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ViewModel vmodel)
{
//Clear TempData (in theory will clear my tempdata when read, so if this controller redirects to another action my tempdata will be clear)
List<SelectListItem> projects = (TempData["Projects"] as List<SelectListItem>);
if (ModelState.IsValid)
{
//...
}
//If it got here it's going back to the screen.
//Repopulate the TempData (allowing it to exist one more trip)
TempData["Projects"] = projects;
vmodel.Projects = projects
return View(atendimento);
}
Is this approach a good one? Is there a better way to achieve that without querying the database every single time?
Thanks a lot!
You don't need to use TempData at all as you have a property in your view model to hold the dropdown items.
public ActionResult Create()
{
ViewModel vmodel = new ViewModel();
vmodel.Projects = GetProjects();
return View(vmodel);
}
private List<SelectListItem> GetProjects()
{
return db.GetProjects(User.Identity.Name)
.Select(x => new SelectListItem { Text = x.Description,
Value = x.Id }).ToList();
}
And in the view
#Html.DropDownListFor(s=>s.ProjectID,Model.Projects)
And in your HttpPost action, If ModelState is not valid, Reload the Projects collection again (because http is stateless)
if(ModelState.IsValid)
{
// to do :Save and redirect
}
model.Projects = GetProjects();
return View(model);
You may cache the Projects so that you do not need to hit the database every time, if you are too much worried about performance.
Personally, I wouldn't worry about querying the database each time for this kind of operation.
What if projects are added/deleted? This could be the reason the save failed (selected project deleted) and the user would never realise it.
I usually write a method to populate all of my view model's SelectListItems and then use this in my Get and in my Post if the validation fails.

MVC 4 how pass data correctly from controller to view

I currently have a controller with a LINQ statement that i am passing data from to my view. I am trying to find a more efficient and better coding method to do this.
My home controller statement is as follows.
Var Melt
Furnace1 =
(from item in db.tbl_dppITHr
where item.ProductionHour >= StartShift && item.ProductionHour <= EndDate
select item).Sum(x => x.Furnace1Total),
ViewData["Furnace1Total"] = Melt.Furnace1;
In my view i then reference the ViewData To show this. Using
#model dynamic
Now i have quite alot of linq statements inside the Index method. And for each one i am doing the ViewData[]
I am hoping that someone can show how i pass more than one var from a controller across to a view without the ViewData or ViewBag methods. And how i would get access to this within my view.
You should create a ViewModel with all of your data needed and then pass that down to the view.
public class ViewModel
{
public List<int> Melt1 { get; set; }
public void LoadMeltProperties()
{
if (Melt1 == null)
{
Melt1 = new List<int>();
}
Melt1 = (from item in db.tbl_dppITHr
where item.ProductionHour >= StartShift && item.ProductionHour <= EndDate
select item).Sum(x => x.Furnace1Total).ToList();
}
public ViewModel Load()
{
LoadMeltProperties();
return this;
}
}
public ActionResult YourControllerAction()
{
var vm = new ViewModel().Load();
return View("ViewName", vm);
}
Then in your View you can use a strongly typed model rather than dynamic
#model ViewModel
You can then iterate over your ViewModel properties via:
foreach(var melt in Model.Melt1) {
// do what you require
}
IMHO, you should create a ViewModel an pass data using it.
Create a class
public class MyViewModel
{
public <MeltFurnace1Type> MeltFurnace1{get;set;}
}
In Action Method
public ActionResult Action()
{
MyViewModel vm = new MyViewModel();
vm.MeltFurnace1 = something;
return View("YourViewName", vm);
}
In View
#model MyViewModel
//You can access your property using
Model.MeltFurnace1
If you need to pass data actually from the controller and its data is depend on internal state or input controller parameters or has other properties of "business data" you should use Model part from MVC pattern:
Model objects are the parts of the application that implement the
logic for the application's data domain. Often, model objects retrieve
and store model state in a database. For example, a Product object
might retrieve information from a database, operate on it, and then
write updated information back to a Products table in a SQL Server
database.
You can see details here or look to the Models and Validation in ASP.NET MVC part of Microsoft tutorial.
Add model class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
}
Pass model object to the view:
public ActionResult Index()
{
var model = GetModel();
return View(model);
}
Add strongly typed View via define model type:
#model Person
Use Model variable in your view:
#Model.City
Use models instead
var Melt
Furnace1 =
(from item in db.tbl_dppITHr
where item.ProductionHour >= StartShift && item.ProductionHour <= EndDate
select item).Sum(x => x.Furnace1Total),
return View("SomeVIew",MeltFurnace1)
In view#model "TypeOfMeltFurnace1"
You can reference model in view by property Model
If someone is still looking for options…
You also can pass object from Controller to View, if you don’t use any particular Model for the View. To pass just a single (or maybe few) parameter from the Controller to the View, you can do it inside View() Method.
In the Controller:
public async Task<IActionResult> Update(int id)
{
return View(new { Id = id });
}
Then in the View you can access your parameter like this (assuming you don’t use any other Model inside your View):
<div>
#Html.ActionLink("Link name", "ControllerMethod", "ControllerName", new { id = (int)Model.Id })
</div>
Otherwise, like already mentioned, pass your model inside View() Method.

Proper way to POST data MVC

Warning: This is my first web app.
I have 4 models, views and controllers. Lets call them A, B, C, D(ex. ModelA, ControllerA, ViewA). They are all basic views with list scaffolding.
/ControllerA/Index
User starts at ViewA and Selects an the first item, which redirects the user to ViewB
/ControllerB/Function?Aid=1
ViewB shows another list based on Selection from ViewA. Then the user Selects again is is redirected to ViewC
/ControllerC/Function?Aid=1&Bid=2
ViewC shows another list based on Selections from ViewA and ViewB. Then the user Selects again is is redirected to ViewD.
/ControllerD/Function?Aid=1&Bid=2&Cid=3
ViewD shows another list based on Selections from ViewA, ViewB, and ViewC, Then the user Selects again.
At this point I would like to POST Aid, Bid, Cid, and Did and save them in my database. Ideally the user would click the link, the data would be posted and then the site would redirect the user back to the homepage. Should I create another model and controller to Handle the post? I thought about trying to do the POST from controllerD but that doesn't seem like the proper way to do this.
The msdn tutorials only show posting directly from a view with a strongly typed model. I kinda stuck and I would prefer not to make this a complete mess.
Edit for Code
Controller
public ActionResult myFunction(int Aid = 0, int Bid, int Cid)
{
//query D stuff here
if (D == null)
{
return HttpNotFound();
}
return View(D.ToList());
}
[HttpPost]
[InitializeSimpleMembership]
public ActionResult CreateQuote(int Aid, int Bid, int Cid, int Did)
{
Quote myQuote = new Quote();
myQuote.Customer_ID_FK = (int)Membership.GetUser().ProviderUserKey;
myQuote.A_ID_FK = Aid;
myQuote.B_ID_FK = Bid;
myQuote.C_ID_FK = Cid;
myQuote.D_ID_FK = Did;
if (ModelState.IsValid)
{
db.Quotes.Add(myQuote);
db.SaveChanges();
db.Quotes.Max();
int mymax = db.Quotes.Max(q => q.ID);
return RedirectToAction();
}
return View(D.ToList());
}
[HttpPost]
[InitializeSimpleMembership]
public ActionResult CreateQuote(Quote myQuote)
{
myQuote.Customer_ID_FK = (int)Membership.GetUser().ProviderUserKey;
if (ModelState.IsValid)
{
db.Quotes.Max();
int mymax = db.Quotes.Max(q => q.ID);
db.Quotes.Add(myQuote);
db.SaveChanges();
return RedirectToAction();
}
return View(D.ToList());
}
It usually makes sense to put your post handler in the controller it's related to. This isn't always the case, as sometimes it would make more sense to make a new controller to handle all posts related to a certain task. You should also understand the distinction between a method IN a controller, and a controller. A controller is just a class that inherits from System.Web.Mvc.Controller and can have methods just like any other class. A perfectly reasonable controller could look like this:
public class DController : Controller
{
//GET /d
public ActionResult Index()
{
//MyModel is a class that would contain the logic to display
//the selections from A, B, and C
var model = new MyModel();
return View(model);
}
//POST /d/saveresults
//We only want this method to accept POST
[HttpPost]
public ActionResult SaveResults(MyEntity data)
{
var model = new MyModel();
model.SaveResultsToDatabase(data);
return Redirect("/");
}
}
The important thing in a controller is to keep logical processing to a minimum. There's nothing wrong with having an if statement here and there, but the majority of your logic should be handled by your model. A controller is there primarily to pass data between your views and models.

How to pass Form Values to the Controller in .NET MVC

In ASP.net MVC:
How should/Can I pass Form data (From the View) to the Controller?
This is the way I am heading :
The Controller Index function is passing a ViewModel object to the View.
The ViewModel object contains a paginated list as well as some SelectLists.
_ The ViewModel object also contains a custom class I named theFilter. This class' purpose is to hold the Filter information Posted from the View via a Form.
I want the Index [AcceptVerbs(HttpVerbs.Post)] function to receive theFilter object populated with the form data, as well as the page number (as is it right now)
Here are snippets of my code:
The Controller/Index postback function:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(int? page, CaseFilter caseFilter)
{
const int pageSize = 10;
var cases = caseRepo.FindAllCases();
var paginatedCases = new PaginatedList<Case>(cases, page ?? 0, pageSize);
return View(new CaseIndexViewModel(paginatedCases, caseFilter));
}
The Filter Class:
public class CaseFilter
{
int iVolume_id = 0,
iSubject_id = 0;
public CaseFilter() {
}
public int Volume_id { get { return iVolume_id; } set { iVolume_id = value; } }
public int Subject_id { get { return iSubject_id; } set { iSubject_id = value; } }
}
And the ViewModel class:
public class CaseIndexViewModel
{
public PaginatedList<Case> PaginatedCases { get; private set; }
public CaseFilter CaseFilter { get; private set; }
public CaseIndexViewModel(PaginatedList<Case> paginatedCases, CaseFilter caseFilter)
{
PaginatedCases = paginatedCases;
CaseFilter = caseFilter;
}
}
Basically I am trying to avoid using Request.Form to populate the Filter class, at least not to use it within the Controller.
Any help, suggestions or disses are welcome!
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection collection)
{
string valueFromNameTextBox = collection["name"];
}
You can index into this collection with the names of all the inputs on the form.
Rather than complicate my method signatures, I've taken to using the ValueProvider property and Try/UpdateModel in the Controller to retrieve form/route values unless the values are simple properties. On the other hand, I would probably also not make the filter part of the model for the View -- I tend to have a narrower conception of the model for the page, wanting it rather to be the business model rather that a model of all the data on the page -- and would simply pass the filter values via ViewData.
To expand BFree's answer, you can go through all the elements in the form by doing something like this:
foreach (string key in collection.keys) {
if (key.contains("blah"))
text1 = collection[key];
}
If it has too many elements for the key.contains if, it can get a bit ugly though, so beware ;).
Finally, I do not need to even use the Request Collection. The CaseFilter object is filled automatically as I set it as a parameter in
public ActionResult Index(int? page, CaseFilter caseFilter)
The code above works as it is.

Categories