MVC3 (Razor) Passing Model Data from View to Controller - asp.net-mvc

I need to know if there is a way to pass the Model (or a part of it, i.e. thereafter a search query) data of a View (Razor Engine) to a Controller.
To explain in a better way what I have to do, that's the interested code:
VIEW:
#model IEnumerable<MvcMovie.Models.Movie>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
...
#foreach (var item in Model) { ...}
...
#Html.ActionLink("Search", "SearchIndex")
#Html.ActionLink("Create Document", "createDoc"/*, new { id = Model.ToList() }*/)
I want to pass the
#model IEnumerable<MvcMovie.Models.Movie>
in the first line (= Model used in the foreach instruction) to the Controller "createDoc" to create a report document dynamically binded with the view.
I tried everything: I tried to use a ViewData (VIEW: ViewData["data"]=Model , CONTROLLER List movies= ViewData["data"]), I similarly tried a TempData, I tried to pass the Model as routeValues in the ActionLink (as you can see: new{ id= Model.toList() }), but nothing worked.
Is it even possible to do the thing I want to?
Can anyone help me?

Your model should not be IEnumerable<MvcMovie.Models.Movie> It should be a class, say SearchMovieModel, that has IEnumerable<MvcMovie.Models.Movie> Movies as one of its properties.
If you want a search model, something like this would be appropriate:
public class SearchMovieModel{
public IEnumerable<MvcMovie.Models.Movie> Movies {get;set;}
public string SearchString {get;set;}
}
you reference this model and its properties in your view and controller.
I guess I should add the method for parsing this in the controller.
On the first call to the view, the model does not exist. You need to create it in your controller:
public ActionResult Search(){
var model = new SearchMovieModel();
//you also need to instantiate the null objects unless you do that in the model's constructor
model.Movies = new List<Movie>();
return View(model);
}
To "reconvert" the POST data back to a model, you need to specify the model and method:
[HttpPost]
public ActionResult Search(SearchMovieModel model){
if (ModelState.IsValid){
//populate your IEnumerable<Movie> here.
return View(model);
}
// the complex collection will not be parsed back into the model. You will need to repopulate it.
model.Movies = new List<Movie>();
return View(model);
}

I think that know what you want... but whit this code
#Html.ActionLink("Create Document", "createDoc", new { id = Model.ToList() })
your html is..
Create Document
and that's because is render the type not the data
Solutions
define filter model to do the search again(the recommendation of jeremy-holovacs and mine)
why ask to the server the same data again? because if someone share that link... you can imagine whats it's the result even inject fake data that your app will generate
serialize data to json for example to forward it to the controller

Related

Best Practice for fill dropdownlist in ASP.NET MVC

I have a question. I fill dropdownlist models in a view model and send them to a view. This is better method rather than using ViewBag.
But when view model is sent to the Controller, this extra data will be sent to the Controller. Now if I use bind attribute for receiving some properties and ModelState is invalid, I should fill them and send to view again?? What is the best practice in these cases?
You can use ViewBag that will help you to avoid such kind of validation.
e.g. you have country data as a dropdown you can bind it to ViewBag and set it in Dropdown and for rest of the part you can supplied your own model which is required to supplied in view.
following is an example with ViewBag and Model.
public class HomeController : Controller
{
[HttpGet]
public ActionResult Home()
{
your Model
ABCModel model = new ABCModel();
// your ViewBag
ViewBag.Country = new SelectList(db.Contries.ToList(),"Id","Name");
return View(model)
}
[HttpPost]
public ActionResult Home(ABCModel model)
{
if(ModelState.IsValid)
{
// your action
}
return View(model);// you can redirect to other page as per your requirements
}
}
Index.cshtml
#model ABCModel // define your required model
bind dropdown like below
#Html.DropDownList("Country",ViewBag.Country as SelectList)

Can i return different objects in views of same action method in Http get and Http Post?

I want to return an object on HTTPGet method and different object in HTTPPost method of the same action method in the controller, but i dont know what to write in the view, which model to get.
Here is the controller code , i have been trying
[HttpGet]
public ActionResult Create()
{
var intSrNo = Convert.ToInt64(TempData["sr_no"]);
MEntities obj_entity = new MEntities();
UDP_get_a_Result obj_proc = obj_entity.UDP_get_a(intSrNo).SingleOrDefault();
return View(obj_proc);
}
[HttpPost]
public ActionResult Create(Table_a obj_a)
{
if (ModelState.IsValid)
{
db.Table_a.AddObject(obj_a);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(obj_a);
}
i'm confused which model to write in view.(Table_a or UDP_get_a_Result) and i want both HttpGet to show values when the page is loaded and HttpPost when the submit is clicked.
View
#model ABC.models.Table_a
#{
ViewBag.Title = "Create";
}
A view can only be strongly typed to a single class. You cannot have different controller actions returning the same view and passing different models to this view. You could use view models: define a class which will hold all the information necessary for this view and then have your controller actions fill this view models and pass it to this view.
I think it would work to have the view typed to some base class (object) and then cast the model to whatever you needed it to be based on get/post. I wouldn't want to maintain it tho. :-D

How to dynamically display a list of both name and value of a String/boolean pair on MVC in ASP.NET MVC

I need to dynamically display a list of both name and value of string/boolean pair on MVC view (*.cshtml) based on user selection. Specifically, both name and value of a string and boolean pair are different in each list. There are more one list that user can select. For example:
FruitName: Apple (string:string)
IsRipen: true (string:boolean)
BookName: C#
IsSold: false
One list type is defined as one report type. A list can be retrieved from report programmatically.
Possible Solution 1
Since the data type of name and value in the list are fixed (string, boolean), one idea is to build a collection as a MVC model, and pass that model to MVC razor view. The question is that how to display the name on the view,
#Html.LabelFor(model => model.Names[0]) //how to display it as 'Fruit Name'
Possible Solution 2
In ASP.NET web form, there is user control whihch can be loaded dynamically. There is partial view in ASP.NET MVC. Can the partial view do what I want? Or is there better solution.
I am new to MVC, any ideal or example would be very much appreicated.
If I understand you correctly, what you want to do is create a Partial View and call it using an action in your controller.
First, do something like this in your controller
// partial
public ActionResult report(string reportName)
{
return View(reportModel.Name);
}
Then, make sure there is a partial view that shares the name of your report.
You can then call the partial view like this:
#{Html.RenderAction("report", "Home", new{ reportName="report" });}
The line above will render the partial view Report.cshtml into the parent view or master layout.
edit
Ok. so every report has a list of name value pairs right?
Assuming that, we can write an action that passes that list to your view.
public ActionResult DisplayPairs()
{
NameValueCollection pairs = new System.Collections.Specialized.NameValueCollection();
pairs.Add("Name", "Value");
pairs.Add("Name2", "Value2");
pairs.Add("Name3", "Value3");
pairs.Add("Name4", "Value4");
return View(pairs);
}
Then we have the DisplayPairs View:
#model System.Collections.Specialized.NameValueCollection
#{
ViewBag.Title = "DisplayPairs";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>DisplayPairs</h2>
<table>
#foreach(string key in Model.AllKeys){
<tr><th>#key</th><td>#Model[key]</td></tr>
}
</table>
Which displays:
Name Value
Name2 Value2
Name3 Value3
Name4 Value4
I hope this helps
Why don't you just create a wrapper class that contains all the data you need?
public class ReportViewModel
{
IEnumerable<KeyValuePair<object, object>> Items { get; set; }
public ReportViewModel()
{ Items = new List<KeyValuePair<object, object>>() }
}
You can then create your model like so:
var model = new ReportViewModel();
model.Items.Add("BookName", "C#");
model.Items.Add("IsSold", false);
return View(model);
In your view, you just iterate over the KeyValuePairs, and print the key and value:
<ul>
#foreach(var kvp in Model.Items)
{
<li>#kvp.Key: #kvp.Value</li>
}
</ul>
(Excuse me if my razor syntax is buggy - I've not worked very much with it as of yet...)
Also, you might have to add calls to ToSting() if you have odd types of objects in your list. I think the framework does that for you if it needs to, but I'm not sure...

ASP.NET MVC - drop down list selection - partial views and model binding

I'm fairly new to ASP.NET MVC and am trying to work out the best way to do this. It's probably simple but I just want to do things correctly so I thought I'd ask.
Lets say I have a model that is this:
Task - Id, Description, AssignedStaffMember
StaffMember - Id, FirstName, LastName
and in my view I want to create a new task. I make a strongly typed Razor view, and can use EditorFor to create textboxes for Description but what about AssignedStaffMember?
I want a drop down list of all current staff and have the option of selecting one, then this gets submitted to an action method which is
NewTask(string description, StaffMember assignedStaffMember)
either that or I could have an int for staffId instead of the StaffMember object and look it up in the action method.
What is the best way to do this? I need to go to the database to get the list off staff, so here's what I thought:
Make a partial view for the listing of staff drop down, which will be used a few times and use #Html.Action("ListStaff", "Staff") to call it. The action method then has
public ActionResult ListStaff()
{
IEnumerable<StaffMember> model = _serviceLayer.GetAllStaff();
return PartialView(model);
}
However I'm not sure on how this will work with model binding, my understanding is that it has to have the correct name for the form to submit it, I'd need to pass the name to the partial view to put on the element I guess?
Instead of having it call a controller to get the staff, make a ViewModel that contains my Task and a IEnumerable possibleStaff collection. possibly send this information to a partial view.
a Html Helper ?
EditorFor could somehow be used?
which one (or is there more) would be best? and how would I do the model binding?
Here is one way to do this. Create a TaskDetailsViewModel
public class TaskDetailsViewModel
{
public TaskDetailsViewModel()
{
this.Task = new Task();
this.StaffMembers = new List<StaffMember>();
}
public Task Task { get; set; }
public IEnumerable<StaffMember> StaffMembers { get; set; }
}
In Controller
public ActionResult Edit(int id)
{
var task = taskRepository.GetTaskByID(id);
var taskDetailsViewModel = new TaskDetailsViewModel();
// Populate taskDetailsViewModel from task and staff
return View(taskDetailsViewModel);
}
[HttpPost]
public ActionResult Edit(TaskDetailsViewModel taskDetailsViewModel)
{
if (ModelState.IsValid)
{
taskRepository.Save(taskDetailsViewModel.Task);
}
else
{
// Show Error
}
return View(taskDetailsViewModel);
}
In View (bound strongly to TaskDetailsViewModel)
#Html.DropDownListFor(model => model.Task.AssignedStaffMember, new SelectList(Model.StaffMembers, "ID", "FirstName", Model.Task.AssignedStaffMember))
#Html.ValidationMessageFor(model => model.Task.AssignedStaffMember)

How to properly populate drop downs from ViewData in controller on multiple views in ASP.NET MVC

I've said this about a million times already, but I'm still new to ASP.Net MVC (and ASP.NET in general) so sorry if this is a bit obvious...
Ok, I have a controller which sends data to two views. One view just lists data from a database, while the other allows you to edit and add records respectively (they use the same view, with different arguments passed in).
The edit view is where my question lies. I have four drop downs in the form that are populated with data from my repository (coming from 3 different tables in my database). This all works 100% absolutely fine with the code below, no errors.
My question is what is the best way to populate these drop downs? Currently, I have my view code like so, passing data through ViewData in the controller:
Edit View:
public ViewResult EditJob(int jobId)
{
IList<JobCust> customerList = jobsRepository.JobCustomers.OrderBy(Customer => Customer.CompanyName).ToList();
ViewData["customers"] = new SelectList(customerList, "CompanyName", "CompanyName");
IList<JobVehicle> vehicleRegList = jobsRepository.JobVehicles.OrderBy(Vehicle => Vehicle.VehicleReg).ToList();
ViewData["vehicleReg"] = new SelectList(vehicleRegList, "VehicleReg", "VehicleReg");
IList<JobVehicle> vehicleTypeList = jobsRepository.JobVehicles.OrderBy(Vehicle => Vehicle.VehicleType).ToList();
ViewData["vehicleType"] = new SelectList(vehicleTypeList, "VehicleType", "VehicleType");
IList<JobDriver> driverList = jobsRepository.JobDrivers.OrderBy(Driver => Driver.LastName).ToList();
ViewData["LastName"] = new SelectList(driverList, "LastName", "LastName");
var job = jobsRepository.Jobs.First(x => x.JobID == jobId);
return View(job);
}
Add View:
public ViewResult AddJob()
{
IList<JobCust> customerList = jobsRepository.JobCustomers.OrderBy(Customer => Customer.CompanyName).ToList();
ViewData["customers"] = new SelectList(customerList, "CompanyName", "CompanyName");
IList<JobVehicle> vehicleRegList = jobsRepository.JobVehicles.OrderBy(Vehicle => Vehicle.VehicleReg).ToList();
ViewData["vehicleReg"] = new SelectList(vehicleRegList, "VehicleReg", "VehicleReg");
IList<JobVehicle> vehicleTypeList = jobsRepository.JobVehicles.OrderBy(Vehicle => Vehicle.VehicleType).ToList();
ViewData["vehicleType"] = new SelectList(vehicleTypeList, "VehicleType", "VehicleType");
IList<JobDriver> driverList = jobsRepository.JobDrivers.OrderBy(Driver => Driver.LastName).ToList();
ViewData["LastName"] = new SelectList(driverList, "LastName", "LastName");
return View("EditJob", new Job());
}
I have that big block of duplicate code there that is bugging me. I know there will be a better solution, but I just don't know what it is. I now want to use this same set of drop downs on another view within this controller, so I'll be duplicating this code for a third time using my current method.
Any ideas? Maybe it's something obvious I'm totally overlooking... Thanks in advance for the help.
You can create a PartialView that accepts as it's model a view model that is designed to contain the data for those four drop down lists.
Then have some service method that returns that view, this may then be called from any controller and then either passed straight to your views, or added as a child object to another view model that then passes it down to your PartialView.
// a view model class to contain the SelectLists that will be rendered as drop downs
public class DropDownViewModel
{
public SelectList Customers{get;set;}
public SelectList VehicleReg{get;set;}
public SelectList VehicleType{get;set;}
public SelectList LastName{get;set;}
}
// another view model that contains a child DropDownViewModel
// object and also the Job object. This is what the Add and Edit Views
// will be responsible for rendering
public class JobsViewModel
{
public DropDownViewModel DropDownViewModel {get;set;}
public Job Job {get;set;}
}
// a service level class that will be responsible for constructing the
// DropDownViewModel object and populating with the required data
public class DropDownService
{
public DropDownViewModel GetDropDownViewModel()
{
// data access code
}
}
Then in your controllers
public ViewResult AddJob()
{
// get the view model
DropDownService service = new DropDownService();
DropDownViewModel dropDownViewModel = service.GetDropDownViewModel();
// create the wrapping JobViewModel that will contain the DropDownViewModel
JobViewModel viewModel= new JobViewModel();
viewModel.Job = new Job();
viewModel.DropDownViewModel = dropDownViewModel;
return View(viewModel);
}
public ViewResult EditJob(int jobId)
{
// get the view model
DropDownService service = new DropDownService();
DropDownViewModel dropDownViewModel = service.GetDropDownViewModel();
// create the wrapping JobViewModel that will contain the DropDownViewModel
JobViewModel viewModel= new JobViewModel();
viewModel.Job = jobsRepository.Jobs.First(x => x.JobID == jobId);
viewModel.DropDownViewModel = dropDownViewModel;
return View(viewModel);
}
In your mark up, you will need to ask your Add/Edit views to pass the model data down to the PartialView which you can do like this:
<% Html.RenderPartial("MyPartialView", Model.DropDownViewModel); "%>
What you're looking for is a ViewModel as described in the Nerd Dinner Tutorial. It's a custom, strongly typed representation of all the data that you need in your view including data to populate dropdowns. Swaffs answer is good and pretty complete. I just wanted to give you another reference. Good luck!

Resources