Submit entity collection with other route parameters - c#

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.

Related

Persisting model property when submitting form

I am trying to figure out why property values are not persisting when I return the View with the model.
Index.cshtml
View:
#Html.HiddenFor(model => model.IsWtv)
Controller:
[HttpPost]
public ActionResult Index([Bind(Exclude = "Id,ShippingCost")] TestModels model)
{
//assume the initial value is false. I only want it to switch to true once
if (!model.IsWtv)
model.IsWtv = true;
return View(model);
}
Model:
public Boolean IsWtv
{
get;
set;
}
The value for IsWtv is always false when I resubmit the form. I don't understand why?
First, you should really be following the PRG pattern (POST, Redirect, GET) when dealing with controller action methods (unless this is some type of AJAX request).
With that said, HtmlHelpers get the model values from the model state and not the model when you update and return the model. In order to update and return the model, add this line of code in your post method prior to any property modifications:
ModelState.Clear();
or you could set the value of IsWtv in the ModelState itself:
//no point in doing any check on the actual variable
//we are always returning true
ModelState["IsWtv"].Value = true;

Persist model field from GET to POST

In the controller's GET method, I pick up a parameter (id) from the query string and assign it to a property in the model, which then gets sent to the view.
The view contains a form in which the user will supply the rest of the values for this model. When the model then gets sent back to the POST method, I need that original parameter (id) back again. But I'm not sure how to persist it. In the POST method, the id field is coming back blank. Is there a better way to do this?
// GET: CreateInsured
[Route("Home/CreateInsured/{id}")]
public ActionResult CreateInsured(int id)
{
Insured insured = new Insured();
insured.PolicyId = id;
return View(insured);
}
// POST: CreateInsured
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateInsured(Insured insured)
{
//insured.PolicyId is empty
}
You should put this Id into a Hidden field, like this:
#Html.HiddenFor(m => m.PolicyId)
And this field should be into your form.

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);
}

Display FormCollection with ASP.NET MVC

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

Categories

Resources