naz786
naz786

Reputation: 495

How to map model data in URL route?

I have an index page which displays Categories (which have attributes: id and name) with the URL request: http://localhost:62745/home/index.

When I click a category, I am taken to http://localhost:62745/Home/Products/6.

I want to make the URL more detailed and replace the Category Id property 6 in the previous URL with the name property of the Category which was just clicked.

My route config look like this:

public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

            routes.MapRoute(
                name: "Categories",
                url: "{controller}/{action}/{categoryName}",
                defaults: new { controller = "Category", action = "Index", categoryName = UrlParameter.Optional }
            );

        }
    }

The first MapRoute() method was already implemented. I added the second one hoping to solve my problem, but it didn't.

This is my controller action for products:

// GET: Product
public async Task<ActionResult> Index(int? id, string categoryName)
{

    var products = (await db.Categories.Where(c => c.Id == id)
                                    .SelectMany(p => p.Products.Select(x => new ProductViewModel { Id = x.Id, Name = x.Name, ByteImage = x.Image, Price = x.Price}))
                                    .ToListAsync());

    categoryName = db.Categories.Where(c => c.Id == id).Select(c => c.Name).ToString();

    if (products == null)
    {
        return HttpNotFound();
    }

    return View(new ProductIndexViewModel{ Products = products, CategoryId = id });
}

Upvotes: 0

Views: 488

Answers (1)

user3559349
user3559349

Reputation:

Both your routes are identical in terms how they are interpreted - they accept 2 segments and an optional 3rd segment, so Home/Products/6 and Home/Products/CategoryName both match the first Default route (there is nothing unique to distinguish the 2nd Categories route so it will never be hit.

Your question refers to a Products() method in the 2nd url, but you have not shown that method, so its not clear if your referring to a product name or a category name, but the principal is the same.

Assuming you want the url to be Home/Products/ProductName to display a specific product, then in the view

@foreach(var item in Model.Products)
{
    // link for each product
    @Html.ActionLink("Details", "Products", "Home", new { id = item.Name }, null)

Then you can simply make the method

public ActionResult Products(string id)
{
    // the value of id is the the the product name
    ....
}

Alternatively, if you want the parameter to be string name rather that string id, then you can define a specific route and place it before the Default route.

routes.MapRoute(
    name: "ProductDetails",
    url: "Home/Products/{name}",
    defaults: new { controller = "Home", action = "Products", name = UrlParameter.Optional }
);

so that the method becomes

public ActionResult Products(string name)

Side note: The fact you have a query in the Index() method to get the category name suggests you might also be wanting a 'slug' route, in which case, refer how to implement url rewriting similar to SO.

Upvotes: 1

Related Questions