tags:

views:

1520

answers:

3

I am displaying an list of Items for a given Order. When a user clicks Add Item I redirect to the Item / Create page. This page collects that necessary input but also needs to know the order id that the item belongs to. What is the appropriate way to pass the OrderID to the Item / Create so that it survives the form post when the newly created item is saved.

I've played with TempData and writing the id out on the detail page via Html.Encode(). It gets me part of the way there in that the id shows up on the item form but the value is lost when the form submits and posts. I suppose because its not part of the formcollection. I am guessing my workaround is not the best way and would like to know if anyone can point out the proper way to do this in asp.net mvc.

A: 

I've used this sequence (sorry if there are mistakes, I took this from a working example and modified it for your question):

1) In the Order.Details view (assume Model is of type Order):

...
<%= Html.ActionLink("Create New Item", "Create", "OrderItem", new { orderId = Model.ID }, null)%>
...

2) In the OrderItem.Create action:

public ActionResult Create(int orderId)
{
    ViewData["orderId"] = orderId;
    return View();
}

3) In the OrderItem.Create view:

...
<% using (Html.BeginForm(new { orderId = ViewData["orderId"] }))
...

4) In the OrderItem.Create POST action:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(int orderId)
{
    // omitted code to create item, associated with orderId

    return RedirectToAction("Details", "Order", new { orderId = orderId });
}

If anyone can think of how to improve on this, or of a better way altogether, please chime in, I'm sort of new to this myself so I'd like to improve.

DSO
+3  A: 

I do this by creating a new route for the Item controller that includes the OrderId. It doesn't make sense to have an Item without an Order, so the OrderId is required using the constraints parameter.

routes.MapRoute(
    "OrderItems",
    "Item/{action}/{orderId}/{id}",
    new { controller = "Item" },
    new { orderId = @"d+" }
);

So the url would look like http://&lt;sitename&gt;/Item/Create/8, where 8 is the OrderId for which to create an item. Something similar could be done for Delete action routes with http://&lt;sitename&gt;/Item/Delete/8/5, where 8 is the OrderId and 5 is the ItemId.

Your Action methods would look like this:

public ActionResult Create(int orderId)

public ActionResult Delete(int orderId, int id)

You could also set it up so that the urls looked like http://&lt;sitename&gt;/Order/8/Item/Create and http://&lt;sitename&gt;/Order/8/Item/Delete/5 if that seems to more clearly show what's going on.

Then the route would look like this:

routes.MapRoute(
    "OrderItems",
    "Order/{orderId}/Item/{action}/{id}",
    new { controller = "Item" },
    new { orderId = @"d+" }
);
Dennis Palmer
Dennis - this looks way better than my idea. One clarification though - how do I get the orderId to survive the Post of the OrderItem Controller. Seems that it wants to use the default route, not the newly created one. [AcceptsVerbs(HttpVerbs.Post)]public ActionResult Create (FormCollection collection)
etechpartner
Make sure the OrderItems route is listed before the default route. That makes a difference. Also, need to include the orderId param in your Create method: [AcceptsVerbs(HttpVerbs.Post)] public ActionResult Create (int orderId, FormCollection collection)
Dennis Palmer
So your Create view's form post url should contain the orderId as in /Order/8/Item/Create
Dennis Palmer
A: 

To round-trip a field that's not part of the normal data entry, I generally use a hidden field in the view, like this:

<%= Html.Hidden("OrderID", Model.OrderID) %>

It looks like a form field, acts like a form field, but the user cannot see it. Make sure you push the correct OrderID into your model from the controller.

Robert Harvey