Insert current URL into #Html.RouteLink parameter to build a link - asp.net-mvc

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.

Related

Partial View different model

I am trying to use a partial view that uses a different model than the one used in the main view. The partial view has
to show a list with the products recently added. But I am stuck on how and where to implement the logic for retrieving the data I need from the database.
Home/Index.cshtml:
#Html.Partial("~/Views/Shared/_LatestProducts.cshtml", new List<Website.Models.LatestProductsList>())
Shared/_LatestProducts.cshtml:
#model List<Website.Models.LatestProductsList>
#foreach (var item in Model)
{
<a href="#" title="img">
<img src="~/Content/images/latest-product-img.jpg" alt="" /><p>#item.ProductName</p>
</a>
}
And I have the following code that I am trying to use in order to get some products for tests and show them in the partial view:
public PartialViewResult _LatestProducts()
{
List<LatestProductsList> latestProd = (from p in db.Products
where p.ID < 5
select new LatestProductsList { ProductName = p.Title }).ToList();
return PartialView(latestProd);
}
I thought that I might use it in the HomeController, but that obviously doesn't work and I am not sure if partial views should have their own controller, if I can
just call it from another class. I am still wrapping my head around ASP MVC, so any help will be appreciate it.
Just call the action that renders the partial view in Index.cshtml.
#Html.Action("_LatestProducts", "Product")
Second parameter is the name of the controller that has the _LatestProducts method.
Just a reminder: Names with _ prefix is for partial views only, not action methods. You should rename it to LatestProducts.

Get id from clicked url

It is necessary for me at a choice the user of a subject of a forum on the page from a database contents of the chosen subject were loaded on the page. I will describe how I approached to the solution of this task. This code presents forum threads:
<a href='Home/ThreadView' id='1'>About all</a>
<a href='Home/ThreadView' id='2'>Cars</a>
ThreadView action has code:
[HttpGet]
public ViewResult ThreadView(int id)
{
ViewData[ThreadViewSelector] = Thread.GetThreadView(id);//Thread is a model.
return View();
}
When I click on any link an error occurs:
Parameters dictionary contains record with value NULL for parameter "id" of type "System.Int32"
How to solve this problem ?
The ID attribute in your link is the id of the HTML element... so, that id could be useful if you want to do something with javascript.
With your current code, the id is never sent to the controller action, that is why the error you are seeing. You need to put the ID in the href attribute along with the Controller/Action.
You have 2 options to pass the id to your controller action:
If you have the out-of-the-box routing rules you can use:
<a href='Home/ThreadView/1' id='1'>About all</a>
You can send it via query string this way:
<a href='Home/ThreadView/?id=1' id='1'>About all</a>
EDIT:
The element id is not required in order to have the routing working.
Instead of doing this manually, you could use one of the Razor Helpers:
#Html.ActionLink("About all", "Home", "ThreadView", new { id = 1})
If you need to add some attributes to the link element, besides the routing parameters:
#Html.ActionLink("About all", "Home", "ThreadView", new { id = 1}, new { #class = "redLink", id="aboutall"})

asp.net mvc 3 Model with complex property and checkboxes

I have a typed view as Item (one class that i created) with a form inside to add Items to my database. This Item class has one property called Categories that is a List (Category has 2 properties ID and Name)
Im using an editorfor in my view:
<div>
#(Html.EditorFor(e => e.Categories, "Categories"))
</div>
I created an EditorTemplatefor called "Categories.cshtml" render all the available categories:
#{
Layout = null;
}
#model List<Category>
#{
foreach (Category category in ((BaseController)this.ViewContext.Controller).BaseStateManager.AvailableCategories)
{
#Html.Label("test", category.Name)
<input type="checkbox" name="Categories" value="#(category.ID)" />
}
}
The checkboxes are well rendered (one for every Available category in cache), but after clicking in some, and post the form, im receiving my instance of Item but with the property Categories empty.
What i have to do to receive my List Categories completely instantiated after submit the form?
Dont loop it. Let the framework generate the code for you (then, it will know how to build it back and bind it to your controller).
Just pass the list to the editor template and mvc will do the rest. Check my blog post on something similar.
Try using an index based loop. This ensures MVC will render the item's attributes in such a way that allows the default model binder to instantiate the model on post back. Also, use the Html helper for the checkbox as well:
var categories = ((BaseController)this.ViewContext.Controller).BaseStateManager.AvailableCategories;
for (var index = 0; index < categories.Count; index ++)
{
#Html.Label("test", categories[index].Name)
#Html.Checkbox("ID", categories[index].ID)
}

ASP.net MVC RouteLink and optional routeValues

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.

asp.net mvc Html.ActionLink() keeping route value I don't want

I have the following ActionLink in my view
<%= Html.ActionLink("LinkText", "Action", "Controller"); %>
and it creates the following URL http://mywebsite.com/Controller/Action
Say I add an ID at the end like so: http://mywebsite.com/Controller/Action/53 and navigate to the page. On this page I have the markup I specified above. Now when I look at the URL it creates it looks like this:
http://mywebsite.com/Controller/Action/53 (notice the addition of the ID)
But I want it to remove the ID and look like it did originally, like this http://mywebsite.com/Controller/Action (notice no ID here)
Any ideas how I can fix this? I don't want to use hard coded URLs since my controller/actions may change.
The solution is to specify my own route values (the third parameter below)
<%= Html.ActionLink("LinkText", "Action", "Controller",
new { id=string.Empty }, null) %>
It sounds like you need to register a second "Action Only" route and use Html.RouteLink(). First register a route like this in you application start up:
routes.MapRoute("ActionOnly", "{controller}/{action}",
new { controller = "Home", action = "Index" } );
Then instead of ActionLink to create those links use:
Html.RouteLink("About","ActionOnly")
The problem is the built in methods take input from the URL you are currently on as well as what you supply. You could try this:
<%= Html.ActionLink("LinkText", "Action", "Controller", new { id = ""}) %>
That should manually wipe the id parameter.
Don't know why, but it didn't work for me (maybe because of Mvc2 RC). Created urlhelper method =>
public static string
WithoutRouteValues(this UrlHelper helper, ActionResult action,params string[] routeValues)
{
var rv = helper.RequestContext.RouteData.Values;
var ignoredValues = rv.Where(x=>routeValues.Any(z => z == x.Key)).ToList();
foreach (var ignoredValue in ignoredValues)
rv.Remove(ignoredValue.Key);
var res = helper.Action(action);
foreach (var ignoredValue in ignoredValues)
rv.Add(ignoredValue.Key, ignoredValue.Value);
return res;
}
If you either don't know what values need to be explicitly overridden or you just want to avoid the extra list of parameters you can use an extension method like the below.
View
The implementation details are in this blog post
I explicitly set the action name as "Action/". Seems a little like a hack but it's a quick fix.
#Html.ActionLink("Link Name", "Action/", "Controller")
Another way is to use ActionLink(HtmlHelper, String, String, RouteValueDictionary) overload, then there are no need to put null in the last parameter
<%= Html.ActionLink("Details", "Details", "Product", new RouteValueDictionary(new { id=item.ID })) %>
The overloads of Html.ActionLink are changed on the later versions of MVC. On MVC 5 and above. This is how to do this:
#Html.ActionLink("LinkText", "Action", "Controller", new { id = "" }, null)
Note I passed "" for id parameter and null for HTMLATTRIBUTES.
I needed my menu links to be dynamic. Rather than implement a lot of extra code and routing for every single page I simple dispensed with the HTML helper.
#item.MenuItemName

Resources