Accessing Route Value of Id inside a Partial View - asp.net

I am trying to access the Route value of id from inside the PartialView. I have a Post, Comments scenario. And I want my Comments partialview to have access to the PostId.
The Url looks like the following:
Posts/Detail/2
Now, how can I access the value 2 inside the Partial View?
UPDATE:
I changed my CommentsController to the following:
public ActionResult Add(string id,string subject,string name,string body)
{
}
But now when I debug it does not even step into the Add method.
UPDATE 2:
My partial view looks like the following:
<div class="title">Add Comment</div>
#using (Html.BeginForm("Add","Comments",FormMethod.Post))
{
#Html.Label("Subject")
#Html.TextBox("subject")
<br />
#Html.Label("Name")
#Html.TextBox("name")
<br />
#Html.Label("Body")
#Html.TextBox("body")
<input type="submit" value="submit" />
}
And here is the Controller:
public ActionResult Add(string id, string subject, string name, string body)
If I remove id from the above controller action then it works.

You could fetch it from RouteData:
In WebForms:
<%
var id = ViewContext.RouteData.Values["id"];
%>
In Razor:
#{
var id = ViewContext.RouteData.Values["id"];
}
After showing your code it seems that in your form you are not passing the id. So modify it like this:
#using (Html.BeginForm("Add", "Comments", new { id = ViewContext.RouteData.Values["id"] }, FormMethod.Post))
{
...
}
or use a hidden field if you prefer:
#using (Html.BeginForm("Add", "Comments", FormMethod.Post))
{
#Html.Hidden("id", ViewContext.RouteData.Values["id"])
...
}
Now you should get the id in your Add controller action.

Inside of your controller, you could put the route data in the view model that gets sent to the view; the view can then pass along its view model (or portions of it) to the partial view. Your views, including partial views, should ideally only need to rely on data provided by their view models to render.
public ActionResult Add(string id, string subject, string name, string body)
{
return View(
new AddedViewModel {
Id = id,
// etc...
}
);
}

Related

ASP.NET MVC Redirect To Action does not render final View

I'm trying this code:-
If no query string supplied to the Index Method then render a Branch Locator View. When a Branch Id is selected in that View, post back to a Redirect To Route Result OR Action Result method and then redirect back to Index with a query string of the selected Branch Id.
I can run through the code successfully without and then with the query string.
I even run through the Index View and can see the Model working however, the Index View does not render, the Branch Selector View remains. Network developer tools shows the correct URL with query string correctly in place when doing the Redirect.
(NOTE: Both methods are on the same controller).
If I add the same query string directly in the Browser address bar it works fine!
I have this code:
[HttpGet]
public ActionResult Index()
{
var querystringbranchId = Request.QueryString["branchId"];
if(!string.IsNullOrEmpty(querystringId))
{
....do stuff like build a model using the branchId...
return View(Model);
}
return View("BranchSelector")
}
[HttpPost]
public RedirectToRouteResult BranchDetails(FormCollection formCollection)
{
var querystringBranchId = formCollection["BranchList"];
var branchId = int.Parse(querystringBranchId);
return RedirectToAction("Index", new { branchId });
}
Try using strongly typed model on the post, and specifying the param as an actual param - Using View models is going to be much better for you.
I have tested the below - It seemed to work as expected for me:
[HttpGet]
public ActionResult Index(int? branchId)
{
if (branchId.HasValue)
{
return View(branchId);
}
return View("BranchSelector");
}
[HttpPost]
public RedirectToRouteResult BranchDetails(MyModel myModel)
{
return RedirectToAction("Index", new { myModel.BranchId });
}
public class MyModel
{
public int BranchId { get; set; }
}
The View:
<div>
#using (Html.BeginForm("BranchDetails", "Home", FormMethod.Post))
{
#Html.TextBox("BranchId","123")
<input type="submit" value="Go"/>
}
</div>
#MichaelLake Thanks to your post I found the problem. I tried your code and sure enough it works as expected. I didn't mention I was using a Kendo Combobox control (!) loaded with the branches. I didn't mention that as the actual data I needed was available in the post method so, thought the issue was with the Controller methods. I had the Kendo control name as BranchList, I changed it to BranchId and it now works with the original code as expected! The Kendo name becomes the element Id and has to match to work.
Many Thanks!
This will work for you. Cheers :D
return RedirectToAction("Index", "ControllerName", new { branchId = branchId});

Multiple forms rendered in one page

In my Index view, I render(action) 3 other views, Create, Edit and List. The Create and Edit views are both forms to manipulate data in a database. When I push edit button in the List View the Create form should disappears and the Edit form should appear. So Create and Edit shouldn't be shown at the same time.
When I don't render the views in one view, everything works fine. But because I don't want to switch pages, I want to render the views in one page. Here is the problem. My Create form and List view works fine when I render them in one page. But when I also want to render my Edit form, I get errors.
One thing that happens is that the Create form puts de data in the database 2 times. Also when I don't put validated data in my form, the edit form appears at the same time that the create form is shown. The third problem is when I put the edit button in my List. The create form disappears and the edit form apears. So this works, but when I want to save the changed data, I get a hex string error. When I restart the application, the data isn't changed, but there is new record with the changed data.
I guess there is a problem with the id string. But I can't fix it.
So I have 4 views (index, create, list and edit)
Create, list and edit all have their own model.
And I use one controller for the views / models
The controller (CarsController):
public class CarsController : BaseController
{
public ActionResult Index()
{
return View();
}
public ActionResult List()
{
List<Car> carsInDb = CarRentalContext.Cars.FindAll().ToList();
return View(carsInDb.ConvertAllToViewModels());
}
[HttpGet]
public ActionResult Create(string id)
{
if (String.IsNullOrEmpty(id))
{
return View();
}
return null;
}
[HttpPost]
public ActionResult Create(InsertCarViewModel insertCarViewModel)
{
if (ModelState.IsValid)
{
Car car = insertCarViewModel.ConvertToDomain();
CarRentalContext.Cars.Insert(car);
Response.Redirect("Cars", true);
}
return View(insertCarViewModel);
}
[HttpGet]
public ActionResult Edit(string id)
{
if (String.IsNullOrEmpty(id))
{
return null;
}
Car car = CarRentalContext.Cars.FindOneById(new ObjectId(id));
return View(car.ConvertToUpdateViewModel());
}
[HttpPost]
public ActionResult Edit(UpdateCarViewModel updateCarViewModel)
{
if (ModelState.IsValid)
{
Car modifiedCar = updateCarViewModel.ConvertToDomain();
CarRentalContext.Cars.Save(modifiedCar);
return RedirectToAction("Index");
}
return View(updateCarViewModel);
}
}
The index view:
#model MvcApplication1.ViewModels.UpdateCarViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
#{Html.RenderAction("Create", Model);}
</div>
<div>
#{Html.RenderAction("Edit", Model);}
</div>
<div>
#{Html.RenderAction("List", Model);}
</div>
</body>
</html>
Instead of show/hide the views for create and edit views try to replace one with another, using ajax to get the result of the action. This way you will have only one view in the page.
Do something like this:
<script type="text/javascript">
function renderActionInDiv(idcar, action)
{
$.ajax({
type: "POST",
url: action, //or "/Cars/Edit"
data: { id = idcar},
success: function (ajxData) {
if (typeof callback !== 'undefined') {
$("#div-editOrCreate").html(ajaxData);
}
},
async: asyncCall
});
}
</script>
<a onclick='renderActionInDiv(3, "Edit")'>edit car<a/>
<a onclick='renderActionInDiv(0, "Create")'>create new car<a/>
<div id="div-editOrCreate">
#{Html.RenderAction("Create", Model);}
</div>

Retaining RouteData in Html.BeginForm()

I have been using a variant of the Html.BeginForm() method to attach an html attribute to my form, like this :
#using (Html.BeginForm("actionname", "controllername", FormMethod.Post, new { id = "myform" }))
Unfortunately this causes the form target to loose all route data.
Say my url was controller/action?abc=123, then using Html.BeginForm() generates the form post target as controller/action?abc=123 but the overloaded version (which I am using to add the html id attribute to the form), generates the target as controller/action (which actually is understandable, since I am specifying the route myself, but it doesn't solve my purpose).
Is there a variant of the Html.BeginForm() which would allow me retain the old route values and let me add html attributes to the form at the same time?
As far as I can see, only the parameterless version of BeginForm uses the current full URL.
public static MvcForm BeginForm(this HtmlHelper htmlHelper) {
// generates <form action="{current url}" method="post">...</form>
string formAction = htmlHelper.ViewContext.HttpContext.Request.RawUrl;
return FormHelper(htmlHelper, formAction, FormMethod.Post, new RouteValueDictionary());
}
I'm not sure if this is the best way, but you could write a custom form helper to include the QueryString values:
public static class MyFormExtensions
{
public static MvcForm MyBeginForm(this HtmlHelper htmlHelper, object htmlAttributes)
{
var rvd = new RouteValueDictionary(htmlHelper.ViewContext.RouteData.Values);
var queryString = htmlHelper.ViewContext.HttpContext.Request.QueryString;
foreach (string key in queryString.AllKeys) rvd.Add(key, queryString[key]);
return htmlHelper.BeginForm(null, null, rvd, FormMethod.Post, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
}
#using (Html.MyBeginForm(new { id = "myform" }))
{
//...
}

Passing JSON data from controller action to a razor view

I would like to know how can I pass data from controller to view in mvc3 razor.
The view is in .cshtml file.
I have a variable in the controller where I am storing the data I need to push to the listbox control in the view.
var results = data.Select(item => new { id = item, label = item.Value, value = item.Key });
Doing this:
return Json(results, JsonRequestBehavior.AllowGet);
Gives me only a popup with the data which needs to be pushed to the listbox:
In the view the listbox resides in accordion control:
<div id="accordion">
#{int i=0;}
#foreach (var item in Model.Parameters)
{
<h3>#Html.LabelFor(m => item.Name, item.Prompt)</h3>
<div>
<div class="editor-field">
<select multiple id="#("Select" +item.Name)" name="#("Select" +item.Name)"></select>
</div>
</div>
i++;
}
</div>
So, I would like to know what should I do to push the items to the listbox instead of showing a popup for the control
Beginner in MVC, Thanks for your understanding.
Thanks in advance, Laziale
EDIT: Json format output
{System.Linq.Enumerable.WhereSelectListIterator<System.Collections.Generic.KeyValuePair<string,string>,<>f__AnonymousType1<System.Collections.Generic.KeyValuePair<string,string>,string,string>>}
returning JSON to your razor view is probably not the best method. I would suggest use a viewModel which is a c# class by itself.
namespace Test
{
public class ViewModel
{
public bool GivingAPresentation { get; set; }
}
}
public class MyController: Controller
{
public virtual ActionResult Index()
{
var model=new ViewModel(){GivingAPresentation =true;}
return View(model);
}
}
Your view code:
#model Test.ViewModel <!-- Note the full namespace -->
<br>GivingAPresentation: #Model.GivingAPresentation </br>
If you are forced to work with a JSON object that is returned from your action then you need to deserialize it first and then work with that object. you can read this post http://www.drowningintechnicaldebt.com/ShawnWeisfeld/archive/2010/08/22/using-c-4.0-and-dynamic-to-parse-json.aspx on how to parse JSON to a c# dynamic object.
Let me know if that helps.

simple MVC form post and url routing question....I hope

I have a view called Associations that has a drop down list and a button. When the user selects an option and presses submit, I want them to go to Association/{associationKey}. Association needs to work on get and post.
Currently, with the code below, when the form is posted, it DOES go to Association and displays the correct record, BUT it does not append the associationKey to the url.
So I am getting:
http://localhost/Association
instead of:
http://localhost/Association/202
If I manually navigate to http://localhost/Association/202 everything works perfectly, so get and post are both working fine....I just want the key in the url after a post!
Surely there must be something super simple I am doing wrong. Relevant code below.
Thanks!
ASSOCIATIONS view:
<% Html.BeginForm("Association", "Staff", FormMethod.Post); %>
<%:Html.DropDownList("associationKey", new SelectList(Model.Associations.ToList(), "AssociationKey", "LegalName"))%>
<input type="submit" value="Edit The Selected Record" />
<% Html.EndForm(); %>
STAFF controller:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Association(int associationKey)
{
return View("Association", new AssociationViewModel(associationKey));
}
GLOBAL.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default", "{action}", new { controller = "Staff", action = "Default" });
routes.MapRoute("Associations", "Associations", new { controller = "Staff", action = "Associations" });
routes.MapRoute("Association", "Association/{associationKey}", new { controller = "Staff", action = "Association" });
}
ASSOCIATION view model:
public class AssociationViewModel
{
public Repository db = new Repository();
public Association Association {get; private set; }
public List TelephoneTypes { get; private set; }
public AssociationViewModel(int associationKey)
{
Association = db.AssociationById(associationKey);
TelephoneTypes = db.ListTelephoneTypes().ToList();
}
}
I think you should separate out your controller actions into a Get action and a POST action like so:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Association(int associationKey)
{
return View("Association", new AssociationViewModel(associationKey));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Association(AssociationViewModel model)
{
return RedirectToAction("Association", new {associationKey= model.associationKey});
}
The MVC framework will automatically bind the selected value from your SelectList to the model (assuming you have a property in the model to hold the selected value). From there you just need to redirect to your GET method passing in the key.
It's doing a post instead of a GET. This puts the value in the form parameters not in the url. You might want to intercept the form submit using javascript and turn it into a GET using location=..
<script type="text/javascript">
$(function() { // requires jQuery
$('form').submit( function() {
// maybe do some validation to ensure a legal value chosen first?
location.href = $(this).attr('action') + '/' + $(this).find('select').value();
return false; // cancel submit
});
});
</script>
<% Html.BeginForm("Association", "Staff", FormMethod.Post); %>
<%:Html.DropDownList("associationKey", new SelectList(Model.Associations.ToList(), "AssociationKey", "LegalName"))%>
<input type="submit" value="Edit The Selected Record" />
<% Html.EndForm(); %>

Resources