渲染局部视图时如何调用控制器操作? [英] How can I call a controller action when rendering a partial view?

查看:17
本文介绍了渲染局部视图时如何调用控制器操作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为侧边栏创建一个局部视图,该视图将显示我网站上最受欢迎的帖子.如何创建一个单独的控制器来加载局部视图所需的模型?(带有热门帖子的 IEnumerable)

I am creating a partial view for a sidebar that will show the most popular posts in my site. How can I create a separated controller for loading the model required by the partial view? (The IEnumerable<Post> with the popular posts)

目前我已经创建了一个控制器类来加载流行的帖子,但是在渲染部分时我不断收到错误,因为我无法调用控制器并加载部分模型.例如,如果我从渲染单个帖子的视图中调用它,模型类型将不匹配(Post vs IEnumerable<Post>)

Currently I have created a controller class that loads the popular posts but I keep getting errors when rendering the partial as I cannot call the controller and load the partial model. For example, if I call it from a view where I render a single post, the model types won't match (Post vs IEnumerable<Post>)

这是我的 SidebarController:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using GoBaron.Front.Models;

namespace GoBaron.Front.Controllers
{
    public class SidebarController : Controller
    {
        private readonly ApplicationDbContext _context;

        public SidebarController(ApplicationDbContext context)
        {
            _context = context;
        }

        public async Task<IActionResult> PopularPosts()
        {
            return PartialView(await _context.Posts
                .Where(p => p.IsActive == true)
                .OrderByDescending(p => p.Id)
                .Take(5)
                .ToListAsync());
        }
    }
}

这是我的部分观点PopularPosts.cshtml:

@model IEnumerable<GoBaron.Front.Models.Post>
@using GoBaron.Front.Data.Extensions

@if (Model.Any())
{
    <div class="sidebar-item" id="topTrending">
        <header class="sidebar-item__header">
            <h1>Najpopularniejsze</h1>
        </header>
        <div class="sidebar-item__content">
            @foreach (var item in Model)
            {
                <a asp-controller="Post" asp-action="Details" asp-route-id="@item.Id" asp-route-slug="@item.Title.ConvertToSlug()" title="@item.Title">
                    <div class="sidebar-post-item" style="background-image:url('@item.PosterUrl')">
                        <span class="sidebar-post-item__counter">+5</span>
                        <span class="sidebar-post-item__title">@item.Title</span>
                    </div>
                </a>
            }
        </div>
    </div>
}

当我想在布局中包含热门帖子侧边栏时,我只需添加:

When I want to include the popular posts sidebar, for example in the layout, I just add:

@await Html.PartialAsync("~/Views/Sidebar/PopularPosts.cshtml")

推荐答案

这是一个很好的 视图组件 而不是局部视图.您不仅需要渲染视图,还需要执行从数据库加载热门帖子的逻辑.

This is a good use case for a View Component instead of a partial view. You not only need to render a view, you also need to execute that bit of logic that loads the Popular Posts from the db.

创建一个新的 ViewComponent 类,而不是 Controller.这包含在其 InvokeAsync 方法中为视图组件准备模型的逻辑,它可以采用任意数量的参数.在您的情况下, InvokeAsync 不带任何参数,将从数据库加载热门帖子并呈现视图组件视图:

Instead of a Controller, create a new ViewComponent class. This contains the logic that prepares the model for the view component in its InvokeAsync method, and it could take any number of parameters. In your case InvokeAsync takes no parameters, will load the popular posts from the db and will render the view component view:

public class PopularPostsViewComponent: ViewComponent
{
    private readonly ApplicationDbContext _context;

    public PopularPostsViewComponent(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var posts = await _context.Posts
            .Where(p => p.IsActive == true)
            .OrderByDescending(p => p.Id)
            .Take(5)
            .ToListAsync();

        return View(posts);
    }
}

现在您需要为该视图组件创建视图.默认视图名称 Default.cshtml (就像您在不指定视图名称的情况下执行 return View() 一样),它应该位于 Views/Shared/Components/PopularPosts/Default.cshtml.这将与您当前的局部视图基本相同:

Now you need to create the view for that view component. The default view name Default.cshtml (as when you do return View() without specifying a view name) and it should be located in Views/Shared/Components/PopularPosts/Default.cshtml. This will basically be the same as your current partial view:

@model IEnumerable<GoBaron.Front.Models.Post>
@using GoBaron.Front.Data.Extensions

@if (Model.Any())
{
    <div class="sidebar-item" id="topTrending">
        <header class="sidebar-item__header">
            <h1>Najpopularniejsze</h1>
        </header>
        <div class="sidebar-item__content">
            @foreach (var item in Model)
            {
                <a asp-controller="Post" asp-action="Details" asp-route-id="@item.Id" asp-route-slug="@item.Title.ConvertToSlug()" title="@item.Title">
                    <div class="sidebar-post-item" style="background-image:url('@item.PosterUrl')">
                        <span class="sidebar-post-item__counter">+5</span>
                        <span class="sidebar-post-item__title">@item.Title</span>
                    </div>
                </a>
            }
        </div>
    </div>
}

然后从任何其他视图,您将呈现您的视图组件而不是部分,传递 InvokeAsync 所需的任何参数.在您的情况下,您不带任何参数,因此它将是:

Then from any other view, you would render your view component instead of a partial, passing any parameters required by InvokeAsync. In your case you take no parameters so it will be:

@await Component.InvokeAsync("PopularPosts")

最后,如果你在 .Net Core 1.1 或更高版本中,你也可以将其作为标签助手调用:

Finally, if you are in .Net Core 1.1 or higher, you can also invoke it as a tag helper:

<vc:popular-posts></vc:popular-posts>

PS.我写了一篇文章,描述了partials、标签助手的用法并查看您可能会感兴趣的组件.

PS. I wrote an article describing usages for partials, tag helpers and view components that you might find interesting.

这篇关于渲染局部视图时如何调用控制器操作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆