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

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

问题描述

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

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)

当前,我已经创建了一个控制器类,可以加载流行的帖子,但是在渲染局部模型时,由于无法调用控制器并加载局部模型,我始终会遇到错误.例如,如果我从渲染单个帖子的视图中调用它,则模型类型将不匹配(PostIEnumerable<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类.这包含在其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.我写了文章来描述部分标签,标记助手的用法并查看您可能会感兴趣的组件.

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

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

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