ASP.net MVC RouteLink and optional routeValues - asp.net-mvc

I'm trying to figure out how to conditionally set a routeValue which is optional.
I have
<%= Html.RouteLink("<<<","Products",new { page=(Model.Products.PageIndex) }) %>
If a visitor clicks on a "category" I only show products of that category, but if there is no "category" I show all products.
These 2 URLs would be valid:
/Products/Page
/Products/Page?category=cars
The RouteLink is in my pager so I thought I could somehow pass the category parameter in the links in the pager in order to persist the category between pages. However I'm not sure how I handle the case where no category is chosen versus when a category is chosen.
I know I can do this:
<%= Html.RouteLink("<<<","Products",new { page=(Model.Products.PageIndex), category=cars }) %>
But is it possible to handle both cases without creating some awkward if statement?

It's merely an idea but can't you just pass an empty category parameter?
<%= Html.RouteLink("<<<","Products",new { page=(Model.Products.PageIndex), category=(ViewData["CategoryName"]) }) %>
And in your productscontroller where you get the page, just check if it exists or not?
public ActionResult Index(int page, string category)
{
ViewData["CategoryName"] = category;
if(!string.IsNullOrEmpty(category)){
//paging with category
}else{
//paging without category
}
return View("Create");
}
Or is that what you mean by "awkward if statement"?

If cars variable is null or empty string, Html.RouteLink method will not add category parameter to URL automatically. You don't need to do extra checking.

Related

Insert current URL into #Html.RouteLink parameter to build a link

I am probably going about this the wrong way as i'm a noob to .NET MVC, but is there a way to insert the current url as a parameter into an #Html.Routelink?
I'm passing a partial view into a page to display a list of subcategories based off of the current category.
Here is my controller logic:
public PartialViewResult SubcategoryMenu(string category)
{
IEnumerable<string> subcategories = repository.Products
.Where(x => x.SubCategory.Category.CategoryName == category)
.Select(x => x.SubCategory.SubCategoryName)
.Distinct()
.OrderBy(x => x);
return PartialView(subcategories);
}
Here is my partial view which I'm trying to get the current category url into:
#model IEnumerable<string>
#foreach (var link in Model)
{
#Html.RouteLink(link, new
{
controller = "Product",
action = "List",
category = "INSERT CURRENT URL HERE",
subcategory = link,
page = 1
}, new { #class = "btn btn-block btn-default btn-lg" })
}
When the page displays, all products and categories are listed, no problem. When I click a Category, the URL is http://localhost/Category2 which is what I want.
Click a category, then all of the associated subcategories display in a separate div based on the linq query in the controller just fine.
However, to properly display the products, the url generated for the subcategory view needs to be http://localhost/Category/Subcategory and no matter how I tweek the #Html.RouteLink or even an #Html.Actionlink, all I can get is http://localhost/Subcategory If i pass in text into the #Html.Routelink for controller= "something" that will display http://localhost/something/Subcategory so I've been trying to get the current URL which matches the required category passed into the subcategory #Html.Routelink with no success.
If there is a way to insert the current URL into the subcategory RouteLink it would solve my woes. If there is a way to accomplish, or a better way to do so, please help.
Thank you all!
RouteLink and ActionLink use the RouteTable to build outgoing URLs, so it is the RouteTable you need to modify to customize URLs, not the RouteLink configuration.
You can do this many ways, but the most flexible way to make URLs that are driven off of database data is to subclass RouteBase.

asp.net sending a List<> of items to controller

I have the following form:
<li>
<% using (Html.BeginForm("TestMethod", "MyController", FormMethod.Post, new {id = "TestMethod"}))
{%>
<%= Html.Hidden("model", Model.MyListOfObjects) %>
<%}%>
Test
</li>
And the javascript function for the onclick is as follows:
function SubmitForm() {
document.forms["TestMethod"].submit();
}
I am trying to pass the list of objects from the view into the controller, but i have yet managed to get this to work. My Controller function is:
[Authorize]
[HttpPost]
public ActionResult TestMethod(List<Objects> model)
{
dynamic Expando = new ExpandoObject();
Expando.test = model;
return View(Expando );
}
When I view the List of objects in the debugger it always displays "System.Collections.Generic.List`1[]" with no actual objects inside.
So my question is what should I be doing to pass a List of objects into a controller?
I have also tried:
<% using (Html.BeginForm("TestMethod", "MyWork", FormMethod.Post, new {id = "TestMethod"}))
{%>
<% int itemx = 0; %>
<% foreach (var x in Model.MyListOfObjects)
{%>
<%= Html.Hidden("model"+"["+itemx+"]", x) %>
<%itemx++; %>
<% } %>
<%}%>
You cannot just put List<object> as action parameter and expect the model binder to be able to automagically guess what object types you want to put there. You will need to write a custom model binder if you wanted to handle multiple sub-types as illustrated in this post.
And if you want to use a single type for the list such as List<MyViewModel> then simply loop through each element of the list (respecting the convention) and for each element build a hidden field for each property that you want to bind.
But since those are hidden fields, I guess that the user is not supposed to modify them. In this case those hidden fields have nothing to do in your view. Let's not reinvent the ViewState that we were all so happy to get rid of when we moved to ASP.NET MVC from classic WebForms. Simply put a hidden field containing an unique id that will allow you to refetch the corresponding list elements in the POST action given this unique id from wherever you fetched them initially (your database or something I suppose).
You need to have one hidden element for each object in the list, and named model[0], model[1], etc.

Hiddenfor not getting correct value from view model

I have a multi-step file import process. I have a hidden form input in my view that I am trying to populate with the "CurrentStep" from the view model.
<% = Html.HiddenFor(model => model.CurrentStep) %>
CurrentStep is an Enum and I always get the default value rather than the one I provided to the view model. on the other hand this gets me the correct value:
<p><% = Model.CurrentStep %></p>
I realise I could just hand code the hidden input but I want to know: what am I doing wrong? Is there a better way to keep track of the current step between POSTs?
What you are doing wrong is that you are trying to modify the value of a POSTed variable in your controller action. So I suppose you are trying to do this:
[HttpPost]
public ActionResult Foo(SomeModel model)
{
model.CurrentStep = Steps.SomeNewValue;
return View(model);
}
and html helpers such as HiddenFor will always first use the POSTed value and after that the value in the model.
So you have a couple of possibilities:
Remove the value from the modelstate:
[HttpPost]
public ActionResult Foo(SomeModel model)
{
ModelState.Remove("CurrentStep");
model.CurrentStep = Steps.SomeNewValue;
return View(model);
}
Manually generate the hidden field
<input type="hidden" name="NextStep" value="<%= Model.CurrentStep %>" />
Write a custom helper which will use the value of your model and not the one that's being POSTed
My solution was to use Darin's second option, because option 1 (clearing from the model state) means hard coding a string (and the naming convention can be tricky with complex models), and wanted to avoid option 3 because I already have so many custom helpers.
<input type="hidden" name="#Html.NameFor(x => Model.SomeId)" value="#Model.SomeId" />
Just a reminder that you can use Html.NameFor to keep things clean.
Make sure you model property has a "set" operator.
This won't get updated on post-back:
#Html.HiddenFor( m => m.NoSeq)
public Class MyModel
{
int _NoSeq;
public NoSeq
{
get { return _NoSeq };
}
}

ASP.NET MVC : How does my action result find the selected entry in a dropdownlist?

I have a dropdownlist that I populate with some stuff:
In my controller
ViewData["SourceModelList"] = new SelectList(_modelService.GetAllModels(), "Id", "Description");
in my view
<% using (Html.BeginForm("Compare", "Home")) { %>
<p>
<%=Html.DropDownList("SourceModelList")%>
</p>
<p>
<input type="submit" value="Compare" />
</p>
<% } %>
And this renders lovely. Now when I post back to my 'compare' action, how do I find out which item was selected in the drop down?
The name "SourceModelList" should correspond with the name of a field in your ViewModel, so that the binder has something to bind the value of the dropdown to.
Alternatively, you can pluck the value out of the FormCollection object, if your view is not strongly-typed.
The NerdDinner Tutorial goes into this process in greater detail:
NerdDinner Step 5: Create, Update, Delete Form Scenarios
http://nerddinnerbook.s3.amazonaws.com/Part5.htm
You can use any of the regular methods for getting items from a form in ASP.NET MVC: FormCollection, Request object, binding to a specific model or having an action which takes a string SourceModelList parameter.
You can do:
int value = Convert.ToInt32(Request.Form["SourceModelList"]);
Or by ModelBinders just making sure that your model have a property
public int SourceModelList {get; set;}
And the ModelBinder will get it for you.
Or, but less likely:
public ActionResult Name(FormCollection f, int SourceModelList)

Creat a list of links from controller actions ASP.NET MVC

Is there a way to make a list of links for each action in controller instead of having to add
<li><%= Html.ActionLink("Link Name", "Index", "Home")%></li>
for each item?
yes there is.
You can either return a SelectList of key value pairs that you can render as anchor tags.
Or you can create a model in the, and this is not the best place for it, controller and return it to the view which you can then itterate through.
public class myAnchorList
{
public string text {get;set;}
public string controller {get;set;}
public string action {get;set;}
}
then in your code create a List<myAnchorList>.
List<myAnchorList> Anchors = new List<myAnchorList>();
Fill the list with data and return.
return View(Anchors).
if you are already passing over a model then you need to add this list into the model you are returning.
Make sense? if not post a comment and i'll try to explain further.
Edit
Let me complete the picture now that i have a little more time.
On the client side you'd have this untested code;
<ul>
<% foreach(myAnchorList item in Model.Anchors){ %>
<li><%= Html.ActionLink(item.text, item.action, item.controller)%></li>
<% } %>
</ul>
In addition to griegs answer, it might be helpful to construct the list of controller actions via reflection. In which case you might want to look at:
new ReflectedControllerDescriptor(typeof(TController)).GetCanonicalActions()
Credit for this answer goes to: Accessing the list of Controllers/Actions in an ASP.NET MVC application

Resources