【问题标题】:ASP.NET Core strategy for returning partial views via AJAX requests通过 AJAX 请求返回部分视图的 ASP.NET Core 策略
【发布时间】:2021-01-22 01:17:48
【问题描述】:

我有一个 ASP.NET Core 5 Web 应用程序,想知道对部分视图和普通视图使用什么策略。

因此,例如,当用户在 Web 浏览器中导航到 /bars 时,我想加载带有 Bar 列表的页面(以及菜单、页眉、页脚和其他共享布局元素) .

但是,如果用户单击 Bars 菜单,我只想重新加载主容器(没有菜单、页眉、页脚等)。

Controller 中,我是否应该为每个带有PartialAction 的页面创建一个Action,如下所示:

public async Task<IActionResult> Index()
{
    return View(await _repository.ListAsync<T>());
}
    
public async Task<IActionResult> IndexPartial()
{
    return PartialView(await _repository.ListAsync<T>());
}

然后用/Bar/IndexPartial调用菜单上的AJAX,把主要动作留给普通视图?相关,我应该为每个操作创建单独的视图吗?

【问题讨论】:

  • 默认情况下,锚标签将导航到您设置的任何路线。为了异步重新加载页面的一部分,您需要编写客户端代码。您必须处理该链接单击,进行 ajax 调用,然后将返回的 HTML 交换到主 div 中。听起来您正在尝试构建“单页应用程序”。如果您希望拥有客户端路由,您可能希望使用 Angular 或 VueJS 等前端反应性框架之一。
  • 我知道客户端应该是主角,但我要问的是如何从服务器端,控制器行为来管理它。
  • 嘿@serge。您还记得我的回答或其他任何一个回答是否解决了这个问题吗?如果是这样,您介意将适当的答案标记为已接受的答案吗?

标签: c# asp.net-core asp.net-core-mvc .net-5 asp.net-core-5.0


【解决方案1】:

并使用“/bar/IndexPartial”调用菜单上的 ajax,将主要操作留给普通视图? (顺便说一句,我应该为每个动作创建单独的视图吗?)

是的,Index 用于正常索引视图的操作。为可能在主视图中更改的内容创建一个名为“_IndexPartial.cshtml”的新局部视图。在这里,您可以将main container 放到您的局部视图中。点击按钮时,使用ajax请求IndexPartial获取返回的部分视图html内容,然后在主视图中替换。

一个简单理解的例子:

Index.cshtml:

@model List<Book>
@{
    ViewData["Title"] = "Home Page";
}

<label>BookList:</label>
<div id="bookpartial">
    @foreach(var book in Model)
    {
        <div>
            @book.Name
        </div>
    }
</div>

<button id="update">Update</button>

@section scripts{
    <script>
        $("#update").click(function () {
            $.ajax({
                method: 'get',
                url: 'Home/IndexPartial',
                success: function (result) {
                    $('#bookpartial').empty();
                    $('#bookpartial').html(result);
                }
            })
        })
    </script>
}

_IndexPartial.cshtml:

@model List<Book>

@foreach (var book in Model)
{
    <div>
        @book.Name
    </div>
}

控制器:

public IActionResult Index()
{
    var books = new List<Book>
    {
        new Book{ BookId = 1, Name = "BookA"},
        new Book{ BookId = 2, Name = "BookB"},
        new Book{ BookId = 3, Name = "BookC"},
    };
    return View(books);
}

public IActionResult IndexPartial()
{
    var newbooks = new List<Book>
    {
        new Book{ BookId = 4, Name = "BookD"},
        new Book{ BookId = 5, Name = "BookE"},
        new Book{ BookId = 6, Name = "BookF"},
    };
    return PartialView("_IndexPartial", newbooks);
}

结果:

【讨论】:

  • 我很想知道点击“书籍”是否在“主页”和“隐私”附近的菜单中
  • 用图片更新了 OP 以更好地反映问题
  • 我认为图像可能使用布局页面之类的东西,但不是局部视图,因为单击菜单按钮时,您可以看到 url 更改和页面重新加载。这也在 asp.net core mvc 模板项目中实现。可以参考doc
  • @mj1313:目前,您重复处理Index.cshtml IndexPartial.cshtml 内部书籍的逻辑。对于预渲染视图,我建议只调用局部视图,例如@await Html.PartialAsync("_IndexPartial", Model) 以集中代码。 (我知道这可能是您的主要观点的辅助,但它是一个简单的改进,有助于演示如何从视图调用部分,以及从动作中返回它。)
【解决方案2】:

这是一个有趣的问题!您为每个页面公开一个操作并为该页面的内部内容返回 PartialViewResult 的一般方法对我来说很有意义。

但是,如果完整视图和部分视图之间的唯一区别是公共元素,那么我将研究集中入口点视图的方法——如果不是入口点操作本身——否则它们会变得非常重复。

下面,我将介绍处理主入口点的基本策略。

注意:鉴于问题的性质,我假设您对实现 AJAX 部分感到满意​​,而只专注于服务器端建筑,我相信这是你问题的核心。显然,现有的答案解决了客户端实现问题,如果这很重要的话。

查看模型

首先,您可以建立一个处理整个页面内容的通用视图模型。这可能包括例如属性您的导航项目。但它也将包含(部分)视图的名称以及该视图的视图模型,因为您可能需要这些来预渲染内部内容:

public class PageViewModel 
{
    …
    public string View { get; set; }
    public object ViewModel { get; set; }
}

然后,每个页面都有单独的视图模型,例如:

public class BarListViewModel 
{
    public Collection<Bar> Bars { get; } = new();
}

控制器

现在,在您的控制器中,您将引入一个建立BarListViewModel 的方法,这样您就无需在入口点以及返回部分视图的操作之间重复您的逻辑:

[NonAction]
public BarListViewModel GetBarListViewModel() 
{
    var viewModel             = new BarListViewModel() 
    {
        Bars                  = …
    };
    return viewModel;
}

有了这个,您现在可以有一个操作来传递您的Bars 页面,类似于您建议的:

public IActionResult Bars() => PartialView(GetBarListViewModel());

但是,您不必为每个部分设置不同的 Index 操作,而是可以使用以下内容

public IActionResult Index(Page page = "Home") 
{
    var viewModel = new PageViewModel 
    {
        View = page.ToString(),
        ViewModel = page switch 
        {
            "Bars" => GetBarListViewModel(),
            _ => GetDefaultViewModel()
        }
    };
    return View("Index", viewModel);
}

这为连接任何入口点提供了一个“调度”,而无需为每个入口点创建新的操作或视图。

查看

最后,在您的Index.cshtml 中,您将有类似以下的内容来根据视图模型动态注入正确的局部视图:

@model PageViewModel

…
<main>
  @await Html.PartialAsync(Model.View, Model.ViewModel)
</main>
…

这将加载一个例如Bars.cshtml 将包含循环通过 BarListViewModel.Bars 属性的逻辑。

注意:这假设您希望在服务器端预呈现您的局部视图。显然,如果您希望通过 AJAX 进行初始加载,则无需传递嵌套的 BarListViewModel,或调用 PartialAsync()

路由

据推测,上述内容将与允许page 作为路由参数传入的路由配置配对——甚至可能为您的入口点烘焙默认的ControllerAction

app.UseEndpoints(endpoints => 
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{page?}", 
        new { controller = "Home", action = "Index" }
    );
});

注意:显然,您需要小心不要用过于贪婪的路由锁定您的 AJAX 调用或其他入口点。以上只是作为如何捕获page 路由参数的基本演示。

结论

这类似于the approach proposed by @User1997,但它允许他们提议的Index.cshtml 和隐含的Index() 操作被重用于所有您的入口点。当然,如果您有多个遵循这种模式的控制器,这甚至可以概括为基本控制器。

【讨论】:

    【解决方案3】:

    您应该为每个视图创建两个视图和一个控制器。 一个部分视图:_IndexPartial.cshtml 和一个普通视图:Index.cshtml。

    部分视图呈现部分视图内容,因此您应该在第一次呈现页面时将“Index.cshtml”包含到普通视图中。这样您就可以消除代码重写。 每次要更改内容时,向 ParitalView 控制器发出请求,并将响应(部分视图)添加到显示部分视图的 html 元素中。

    @model List<T>
    @{
        ViewData["Title"] = "Test";
    }
    
    <label>Partial Content</label>
    <div id="partialContent">
        @await Html.PartialAsync("_IndexPartial.cshtml", Model)
    </div>
    
    <button id="UpdateContent">Update</button>
    
    @section scripts{
        <script>
            $("#UpdateContent").click(function () {
                $.ajax({
                    method: 'get',
                    url: 'Home/IndexPartial',
                    success: function (response) {
                        $('#partialContent').html(response);
                    }
                })
            })
        </script>
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-10
      • 2020-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多