4

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);
        }
3
  • 1
    I believe this link might be applicable. Please review, and let us know the results: stackoverflow.com/a/47065308/421195. ALSO: please copy/paste text whenever possible. Screenshots are discouraged on SO: meta.stackoverflow.com/questions/303812 Commented Aug 6, 2021 at 19:06
  • The successor of ASP.NET MVC (not Core) Html.Action and Html.RenderAction are View Components, see learn.microsoft.com/en-us/aspnet/core/mvc/views/… Commented Aug 6, 2021 at 19:09
  • Well noted @paulsm4. I used the screenshot as a way to show what the error message was. A copy of the code was given as well. I am working on it and will update when I implement a possible solution. Commented Aug 6, 2021 at 23:37

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

1

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;}
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.