Search code examples
c#razorasp.net-core-mvcasp.net-core-7.0

Fix a root problem when trying to go from Home Index page to another area's page


I build an online store as an ASP.NET Core 7 MVC application. I used areas to separate different parts of it.

My home index is outside the Areas folders. This home index page displays products. Each product has an <a/> tag to redirect to a detail page.

This detail page is put in a subfolder of the Areas folder. For some reason I don't understand, I'm correctly redirected to my detail page but it is completely dark. I thought I put all the necessary attributes in the <a/> and I don't find a solution for this.

Would you see my code below in order to identify how to fix the problem please?

This is how my folders are organized:

wwwroot
Areas
- Admin
- Identity
- ProductDetails
  - Views
    - Product
      - Details.cshtml
  - ProductDetailsController.cs
    Pages
- Index.cshtml
  Program.cs

This is the ProductDetails/ProductDetailsController.cs:

using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using TheCarHub.Areas.Admin.DTO.Read;
using TheCarHub.Data;

namespace TheCarHub.Areas.ProductDetails
{
    [Area("ProductDetails")]
    public class ProductDetailsController : Controller
    {
        private readonly ApplicationDbContext _context;
        private readonly IMapper _mapper;

        public ProductDetailsController(ApplicationDbContext context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }

        // GET: ProductDetails/Product/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null || _context.Car == null)
            {
                return NotFound();
            }

            var car = await _context.Car
                .Where(c => c.Id == id)
                .Include(x => x.CarDetails)
                .Include(x => x.CarDetails.CarMakes)
                .Include(x => x.CarDetails.CarModel)
                .Include(y => y.CarImages)
                .FirstOrDefaultAsync();

            if (car == null)
            {
                return NotFound();
            }

            var carObject = (car, car.CarImages.First(), car.CarDetails);
            CarDtoRead carDtoRead = _mapper.Map<CarDtoRead>(carObject);

            return View(carDtoRead);
        }
    }
}

This is the Pages/Index.cshtml page, first attempt:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<div>
    @foreach (var item in Model._carDtoReadList)
    {
        <figure>
            <a asp-controller="ProductDetails" asp-action="Details" asp-route-id="@item.Id">
                <img src="@Url.Content(item.UrlImage.Replace("wwwroot", "~"))" alt="@item.Name" />
                <figcaption>
                    <p>
                        @Html.DisplayFor(modelItem => item.Name)
                    </p>
                    <p>
                        @Html.DisplayFor(modelItem => item.Year)
                    </p>
                    <p>
                        @Html.DisplayFor(modelItem => item.Description)
                    </p>
                    <p>
                        @Html.DisplayFor(modelItem => item.SellingPrice)
                    </p>
                </figcaption>
            </a>
        </figure>
    }
</div>

For that first result:

First result problem

And my second attempt:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<div>
    @foreach (var item in Model._carDtoReadList)
    {
        <figure>
            <a asp-area="ProductDetails" asp-controller="ProductDetails" asp-action="Details" asp-route-id="@item.Id">
                <img src="@Url.Content(item.UrlImage.Replace("wwwroot", "~"))" alt="@item.Name" />
                <figcaption>
                    <p>@Html.DisplayFor(modelItem => item.Name)</p>
                    <p>@Html.DisplayFor(modelItem => item.Year)</p>
                    <p>@Html.DisplayFor(modelItem => item.Description)</p>
                    <p>@Html.DisplayFor(modelItem => item.SellingPrice)</p>
                </figcaption>
            </a>
        </figure>
    }
</div>

And here's the result for the second attempt:

Second result problem


Solution

  • I'm not sure how your area routing is configured, but from the structure and url you provided, it looks like your configuration is incorrect.

    I made a simple example, you can use it as a reference.

    Models:

    public class Car
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public CarDetail CarDetails { get; set; }
    }
    
    public class CarDetail
    {
        public int Id { get; set; }
        public int CarId { get; set; }
        public string Description { get; set; }
        public Car Cars { get; set; }
    }
    

    ApplicationDbContext:

    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    
        public DbSet<Models.Car> Car { get; set; }
        public DbSet<Models.CarDetail> CarDetails { get; set; }
    }
    

    Index.cshtml.cs:

    public class IndexModel : PageModel
    {
        private readonly ApplicationDbContext _context;
    
        public IndexModel(ApplicationDbContext context)
        {
            _context = context;
        }
    
        public List<Car> carDtoReadList { get; set; }
    
        public IActionResult OnGet()
        {
            carDtoReadList = _context.Car.ToList();
            return Page();
        }
    }
    

    Index.cshtml:

    @page
    @model IndexModel
    @{
        ViewData["Title"] = "Home page";
    }
    
    <div>
        @foreach (var item in Model.carDtoReadList)
        {
            <div>
                <figure>
                    <a asp-area="ProductDetails" asp-controller="ProductDetails" asp-action="Details" asp-route-id="@item.Id">
                        <figcaption>
                            <p>
                                @Html.DisplayFor(modelItem => item.Name)
                            </p>
                            <p>
                                @Html.DisplayFor(modelItem => item.Description)
                            </p>
                        </figcaption>
                    </a>
                </figure>
            </div>
        }
    </div>
    

    Area structure:

    enter image description here

    ProductDetailsController:

    [Area("ProductDetails")]
    public class ProductDetailsController : Controller
    {
        private readonly ApplicationDbContext _context;
    
        public ProductDetailsController(ApplicationDbContext context)
        {
            _context = context;
        }
            
        public IActionResult Index()
        {
            return View();
        }
    
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null || _context.Car == null)
            {
                return NotFound();
            }
            var car = await _context.Car
                .Where(c => c.Id == id)
                .Include(x => x.CarDetails)
                .FirstOrDefaultAsync();
    
            if (car == null)
            {
                return NotFound();
            }
    
            return View(car);
        }
    }
    

    Details.cshtml:

    @model TestApp.Models.Car
    
    <div>
        <h1>Details</h1>
        <div>
            <label>@Html.DisplayNameFor(modelItem => Model.Name) :</label>
            <span>@Html.DisplayFor(modelItem => Model.Name)</span>
        </div>
        <div>
            <label>@Html.DisplayNameFor(modelItem => Model.Description) :</label>
            <span>@Html.DisplayFor(modelItem => Model.Description)</span>
        </div>
        <div>
            <label>@Html.DisplayNameFor(modelItem => Model.CarDetails.Description) :</label>
            <span>@Html.DisplayFor(modelItem => Model.CarDetails.Description)</span>
        </div>
    </div>
    

    Program.cs:

    //...
    builder.Services.AddRazorPages();
    builder.Services.AddControllersWithViews();
    //...
    app.MapControllerRoute(
        name: "MyArea",
        pattern: "{area:exists}/{controller=ProductDetails}/{action=Index}/{id?}");
    
    app.MapRazorPages();
    app.Run();
    

    Test Result:

    Index:

    enter image description here

    Details:

    enter image description here

    For more details about Areas, you can check this document: Areas in ASP.NET Core.