Display FormCollection with ASP.NET MVC - c#

I have a form using Html.BeginForm() on a view. I have in the controller an ActionResult to handle the post. What I need is to just kick back the results to a view. I can kick off the new view, but I don't know how to pass the data to it and once there I don't know how to display it. Here's what I have in the ActionResult.
[HttpPost]
public ActionResult Index(FormCollection collection)
{
ViewBag.Title = "Confirm your order";
return View("OrderConfirmation", collection);
}
If I just do a return View("OrderConfirmation"); it will go to the view so I know I got that working. I just don't know how to pass the data. Right now I have it strongly typed to the same model the form was which causes errors because this FormCollection is not the same obviously. If I remove the strongly typed line the above works, but I have no idea how to loop through the collection at that point.
Thanks for the help.

Use a ViewModel and a stongly typed view. Then you can pass the model to the second view.
public ActionResult Index(Order order)
{
return View("OrderConfirmation", order);
}
ASP.NET MVC will automatically create an order instance and fill the properties from the posted FormCollection.

First don't use FormsCollection, its too generic. You only need it if you need to unit test and access UpdateModel().
Bind to a model type or bind to params:
public ActionResult Index(SomeModel model)
{
return View("OrderConfirmation", model);
}
or
public ActionResult Index(int key)
{
SomeModel model = new SomeModel();
UpdateModel(model);
return View("OrderConfirmation", model);
}
in your view at the top specify
#model MyAppNameSpace.ViewModels.SomeModel

Related

Wrong data type being passed to Partial View?

I'm trying to make a simple chat app to get started with ASP.NET and my partial view seems to be receiving the data type passed to Index.cshtml instead of the data I intended to pass to PersistantMessagesPartial.cshtml.
Error:
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List1[WebChat.Areas.Identity.Data.AppUser]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.List1[WebChat.Models.MessageModel]'.***
In HomeController.cs:
public async Task<IActionResult> Index()
{
Debug.WriteLine("debug1");
return View(await _AppUserDBContext.userList.ToListAsync());
}
public async Task<IActionResult> _PersistantMessagesPartial()
{
Debug.WriteLine("debug2");
return PartialView("_PersistantMessagesPartial", await _MessageModelDBContext.messageList.ToListAsync());
}
In _PersistantMessagesPartial.cshtml:
#model List<WebChat.Models.MessageModel>
<script>
console.log("test");
</script>
#foreach(var item in Model)
{
<script>
console.log("MessagePartial");
console.log(#item.Contents);
</script>
}
How can I render the partial, in _Layout.cshtml:
<partial name="_PersistantMessagesPartial" />
Index.cshtml receives a list of AppUser, and that works correctly. I'm not sure how to make _PersistantMessagesPartial receive a list of MessageModel instead of the list of AppUser.
Instead of passing the list of objects I would create a class:
public class PersistantMessagesPartialModel{
public List<WebChat.Models.MessageModel> Messages{get;set;}
}
and pass the object using for attribute like this:
#model IndexViewModel
<partial name="_PersistantMessagesPartial.cshtml" for="PersistantMessagesPartialModel" />
Then your partial view would look like:
#model PersistantMessagesPartialModel
#foreach(var item in Model.Messages)
{
<script>
console.log("MessagePartial");
console.log(#item.Contents);
</script>
}
The controller would look like this:
[HttpGet]
public async Task<IActionResult> Index()
{
var viewModel = new IndexViewModel(){
PersistantMessagesPartialModel = new
PersistantMessagesPartialModel(){
Messages = await
_AppUserDBContext.userList.ToListAsync()
};
return View(viewModel);
}
The IndexViewModel would look like:
public class IndexViewModel{
public PersistantMessagesPartialModel
PersistantMessagesPartialModel {get;set;}
}
I know that ASP.NET MVC allows you to define a model of the given page as a list of objects however I believe in the rule that each .cshtml page should have a single model class. It gives you huge flexibility when you want to add some new logic to the view. Just imagine a simple case when you want to add some if statement in your partial view which would work based on some value set in the backend of your app. In such a case you would need to use ViewBag, ViewData objects to pass it to the view and this is a bit harder to maintain especially if your app is big.
In my solution, you would just add this extra field to the PersistantMessagesPartialModel, and even in a case when you want to do some renaming etc. its just faster and safer.

Submit entity collection with other route parameters

I have a strongly typed view
#model IEnumerable<MagazineIndex>
that represents user input array of objects.Also I have a dropdown:
#Html.DropDownList("DropDownName",
(List<SelectListItem>)ViewData["magazines"],
new { id = "DropDownName" })
When I submit form I get this error:
There is no ViewData item of type 'IEnumerable' that has the key 'DropDownName'.
My controller is like this:
public ActionResult CreateContent(IList<MagazineIndex> indexes,
string DropDownName)
How must I correct bind values?
Make sure that in your HttpPost action you are populating the ViewData["magazines"] the same way you did in your Get action:
[HttpPost]
public ActionResult CreateContent(IList<MagazineIndex> indexes, string DropDownName)
{
...
ViewData["magazines"] = ... same stuff as in your GET action
return View(indexes);
}
This is only necessary to be done if you intend to redisplay the same view in your POST action. If you redirect, you don't need it. The reason you need it is more than obvious. Your view needs to render a DropDown control which depends on its value.

use id from url directly in a view

Lets say that i have an URL that looks something like this: localhost/userdetails/5 where 5 is the users ID. Is there any way to make use of the ID directly in the view (razor viewengine) and show the details? Or do i handle it in the default action in the controller?
To keep things simple now, focusing on getting the id to the view, you basically want to use the id to populate your model with data and then pass that to the view. So in your controller:
public ActionResult Index(int id)
{
UserModel model = db.Users.Where(u => u.Id == id).SingleOrDefault();
return View(model);
}
The view (very simplified) might look like this:
#model MyProject.Models.UserModel
#Html.DisplayFor(m => m.Id)
#Html.DisplayFor(m => m.Username)
This is very basic though. Eventually, you'll get to a point where you realise you should use viewmodels for your views instead of a domain model that's come directly from the data source. That in itself gives you another problem to solve in the form of mapping properties from the domain model onto your viewmodel. Typically, AutoMapper or ValueInjecter are used for that. For now though, it's probably best to just focus on passing data to a view and getting it back into a controller so that you can do something with it.
Update
This is a simple scenario which demonstrates how to get the data back into the controller. So basically, you'd have a form which you would submit:
#using (Html.BeginForm("Index", "Home"))
{
// Form elements and submit button
}
That would post the data to this action method for you to do whatever you wish with the data:
[HttpPost]
public ActionResult Index(UserModel inputModel)
{
// Check to see if the model's data was valid.
if (ModelState.IsValid)
{
// Do something in the database here.
// Then redirect to give the user some feedback.
return RedirectToAction("Thanks");
}
// The model validation failed so redisplay the view.
return View(inputModel);
}
you can use this in both the controller or in the View as an extension method.
Example: asuming your routes id holder has the default values in global.asax
public int IdFromAdress(HttpContext httpContext)
{
RouteData rd = httpContext.Request.RequestContext.RouteData;
string stringId = (string)rd.Values["id"];
return int.Parse(stringId);
{
You can get the id with this
#HttpContext.Current.Request.RequestContext.RouteData.Values["id"].ToString()
But I would reccomend to use a ViewMdoel to pass the value to the view and not the ViewBag or accessing directly from the view
You should use the model (i.e. the model passed back to your view). A ViewBag is another option but since the ID is part of the model itself, it wouldn't make any sense to do that.
View
#model User
#{
ViewBag.Title = "User Details";
}
#Model.Id;
Controller
public ActionResult UserDetails(int id)
{
return View("UserDetails", (object)id);
}
Yes you can. There is more than one way to do it, but since you've tagged your post MVC, assume you'll want to do it the 'MVC way', which means (imo) using a view model.
So you write a view model
public class MyViewModel()
{
public int ID {get; set;}
}
You populate the model in the controller and pass it to the view
public ActionResut MyView (int id)
{
var viewModel = new MyViewModel {ID = id};
return View (viewModel);
}
Then you have a strongly typed view (strongly typed to the MyViewModel, that is)
and you can reference the model's properties
#Model.ID
Then to make this useful, you can add whatever other properties you're wanting to work with to your view model. Then you can populate them in your controller before rendering the view (to show user info, for example), or let the user populate them for you in the view (using textboxes and such wrapped in a form). Then you can collect the user input in the post action in the controller like so
[HttpPost]
public ActionResult MyView(MyViewModel viewModel)
{
//do stuff with the data from the viewModel
}

Linking Controller behaviour in MVC 3

Is it possible from a Controller to show a view, and then dependant on what that user selects in dropDownList - render another different view back in the original calling controller? Kind of a "daisy-chaining" effect.
The thinking behind this - is a user selecting a vehicle type - (associated with an ID number) in a view, back in the Controller dependant on what was chosen will render another view immediately displaying HTML according to the vehicle type they chose e.g. an HTML page for car or a boat or aeroplane etc...
If this is possbile can someone point me to a code examaple?
Actual Database Model below - but it is for documents, not vehicles!
check the method paremetares of your action method and return different views baed on that . Something like this.
public ActionResult GetInfo(string id,string vehicleTypId)
{
if(String.IsNullOrEmpty(vehicleTypeId))
{
var vehicle=GetVehicleType(vehicleTypId);
return View("ShowSpecificVehicle",vehicle) ;
}
var genericVehicle=GetVehicle(id);
return View(genericVehicle);
}
EDIT : Saying so, I seriously think you should keep those in 2 seperate Action methods. That makes your code clean and better readable. You may move the common functionality to a function and call if from bothe the action methods id needed. So i would do it in this way
Assuming you have a ViewModel for the first page( displays all vehicletypes)
public class VehicleTypesViewModel
{
//other relevant properties
public IEnumerable Types { set;get;}
public int SelectedTypeId { set;get;}
}
Your GET request for the initial view will be handled by this action result.It gets all the Vehicle types and return that to your view in the ViewModels Types property.
public ActionResult VehicleTypes()
{
VehicleTypesViewModel objVM=new VehicleTypesViewModel();
objVM.Types=dbContext.VehicleTypes.ToList();
return View(objVM);
}
and in your View called VehicleTypes.cshtml,
#model VehicleTypesViewModel
#using(Html.BeginForm())
{
#Html.DropDownListFor(Model.SelectedTypeId,new SelectList(Model.Types,"Text",Value"),"Select")
<input type="submit" value="Go" />
}
Another Action method to handle the form post. You have the selected type id here and you can get the specific details here and return a different view
[HttpPost]
public ActionResult VehicleTypes(VehicleTypesViewModel model)
{
// you have the selected Id in model.SelectedTypeId property
var specificVehicle=dbContext.Vehicles.Where(x=>x.TypeId=model.SelectedTypeId);
return View("SpecificDetails",specificVehicle);
}
Alternatively you can do a Get request for the specific vehicle using RedirecToAction method. I would prefer this approach as it sticks with the PRG pattern.
[HttpPost]
public ActionResult VehicleTypes(VehicleTypesViewModel model)
{
int typeId=model.SelectedTypeId;
return RedirectToAction("GetVehicle",new {#id=typeId});
}
public ActionResult GetVehicle(int id)
{
var specificVehicle=dbContext.Vehicles.Where(x=>x.TypeIdid);
return View(specificVehicle);
}
With Javascript : You can do a get call to the new view from your javascript also. without the HTTPpost to controller. You should add some javascript in your initial view for that
#model VehicleTypesViewModel
//Include jQuery library reference here
#Html.DropDownListFor(Model.SelectedTypeId,new SelectList(Model.Types,"Text",Value"),"Select")
<script type="text/javascript">
$(function(){
$("#SelectedTypeId").change(){
window.location.href="#Url.Action("GetVehicle","Yourcontroller")"+"/"+$(this).attr("id");
});
});
</script>
I think to get a better user experience create a partial view, and load that partial view in a div in the same page via an ajax call.
public ActionResult GetVehicalInfo(string id, string vehicleType)
{
var vehicle = GetVehicleType(id, vehicleTypId);
return PartialView("vehicle);
}

Pass UserId to the Model

So I have a simple web app where I can create a user (UserModel) and it returns that user ID. The user then has the ability to enter multiple instances or records of a specific item type, in this case a geographic location (LocationModel). The LocationModel has a UserId property that I need to set when the location is created.
The ActionLink for creating a user is User/Create/id=[userid]
The code in the controller is currently.
[HttpGet]
public ActionResult Create(string id)
{
return View();
}
[HttpPost]
public ActionResult Create(LocationModel model)
{
//CODE TO SAVE MODEL
}
My questions is how to pass the user id so that I can get it into the model in order to relate the two. I was going to try to pass it in the ViewBag and render it in a Hidden field but was thinking that someone has to have a more elegant solution than this. Thanks in advance.
Put the UserId on the model, and pass the model to the view:
[HttpGet]
public ActionResult Create(string id)
{
return View(new LocationModel{UserId = id});
}
Then render the UserId property as a hidden field in the View, so it will get included on your model parameter when the form is POSTed.
#model LocationModel
#using (Html.BeginForm(...)) {
#Html.HiddenFor(m => m.UserId)
...
}

Categories

Resources