Sinado123
Sinado123

Reputation: 93

ASP.NET Core 5.0 MVC : CS1061 error using Html.RenderAction

I am working on a project using ASP.NET Core 5.0 to build a web app (MVC). I tried to use the Html.RenderAction extension method but I get the following error.

enter image description here

COMPILE ERROR:

Html.RenderAction  CS1061: IHtmlHelper<dynamic> does not contain a definition for 'RenderAction'

What I'm I not doing right?

To give more context, I want to render a partial view in _Layout.cshtml using data loaded directly from the database.

This is the content of my _Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384- EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<link rel="stylesheet" href="~/dist/css/main.css" media="all" />
<title>@ViewData["Title"]: my webpage</title>
</head>
<body id="body">
<!-- Header -->
<header id="header">
    <!-- Navigation -->
    <nav id="nav">
        <partial name="../Shared/Components/_navBar.cshtml" />
        <partial name="../Shared/Components/_headerSearchBar.cshtml" />
    </nav>

    @{ Html.RenderAction("GetCategories"); }

</header>

<!-- Main -->
<main role="main" class="main-wrapper">
    <div class="home main-content">
        @RenderBody()
    </div>
</main>

<!-- Footer -->
<footer class="footer">
    <partial name="../Shared/Components/_footer.cshtml" />
</footer>
<script src="~/js/script.js" asp-append-version="true"></script>
@*<script src="~/js/scriptanim.js" asp-append-version="true"></script>*@
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

This is the action method in the controller that loads data to the partial view

        public async Task<IActionResult> GetCategories()
        {
            var categories = await _fashionBuyRepository.GetAllCategoriesAsync();

            return PartialView("_productCategoriesHeader", categories);
        }

Upvotes: 4

Views: 4425

Answers (3)

Sinado123
Sinado123

Reputation: 93

I took the ViewComponent method as suggested by @Michael to solve this problem and was the most optimal for me.

I created a CategoriesViewComponent in a Components folder as follows:

using eFashionBuy.Repository;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace eFashionBuy.Components
{
    public class CategoriesViewComponent : ViewComponent
    {
        private readonly IFashionBuyRepository _fashionBuyRepository;

        public CategoriesViewComponent(IFashionBuyRepository fashionBuyRepository)
        {
            _fashionBuyRepository = fashionBuyRepository;
        }

        public async Task<IViewComponentResult> InvokeAsync()
        {
            var categories = await _fashionBuyRepository.GetAllCategoriesAsync();

            return View(categories);
        }
   }
}

The view associated with this component is called Default.cshtml (a partial view) in the location /Shared/Components/Categories/Default.cshtml as follows:

@model IEnumerable<Category>;

@* Product Categories mobile menu *@
@foreach (var category in Model)
{
    <li class="categories__item">
        <a class="categories-link"
           asp-controller="Catgeories"
           asp-action="Category"
           asp-route-id="@category.CategoryID"
           asp-route-category="@category.CategoryName">

           <span>@category.CategoryName</span>
        </a>
    </li>
}

The component is now ready for use. I simply called it as follows where I want to use it

<ul class="categories__items">  
    <!-- This is how I call the component using TagHelper -->  
    <vc:categories />
</ul>

This helped me avoid most of the nuances with RenderAction which was my initial approach. I will use this knowledge to simplify future designs.

Upvotes: 2

Serge
Serge

Reputation: 43860

There are several ways to do this. One of them to use component. But it will need some javascript code to use it

Another, more simple for a novice way, you have to create a base view model that you will have to use for ALL your views that are using this layout

using Microsoft.AspNetCore.Mvc.Rendering;

public interface IBaseViewModel
{
     int CategoryId { get; set; }
     List<Category> Categories{ get; set; }
}

public class BaseViewModel : IBaseViewModel
{
    public int CategoryId { get; set; }
    public List<Category> Categories { get; set; }
}

action

public async Task<IActionResult> Index()
{
 var baseViewModel=new BaseViewModel();
 baseViewModel.Categories=  await _fashionBuyRepository.GetAllCategoriesAsync();
 return View(baseViewModel);
}

use this model inside of the partial view

@model BaseViewModel

layout

@model IBaseViewModel // you can omit it but I like to have it explicitly

@if(Model!=null && Model.Categories!=null && Model.Categories.Count > 0)
{
 <partial name="_productCategoriesHeader" />
}

for another view you can create this action code

public IActionResult MyAction()
var myActionViewModel= new MyActionViewModel {
..... your init code
}

 InitBaseViewModel(myActionViewModel);

return View(myActionViewModel)
}

public class MyActionViewModel : BaseViewModel
//or 
public class MyActionViewModel : IBaseViewModel
{
    public .... {get; set;}
}

Upvotes: 1

Loupeznik
Loupeznik

Reputation: 313

In .NET 5, @Html.RenderAction no longer works as to my knowledge.

I believe you can use

@await Html.PartialAsync("GetCategories")

or

@await Html.RenderPartialAsync("GetCategories")

instead of it. There may be other options, check the .NET documentation.

Upvotes: 3

Related Questions