【问题标题】:How to design an MVC5 Global Search feature in Layout.cshtml如何在 Layout.cshtml 中设计 MVC5 全局搜索功能
【发布时间】:2016-09-15 08:28:08
【问题描述】:

我正在尝试在我们应用程序的所有视图中实现“全局搜索”功能,该功能位于主菜单上方。它看起来像这样:

“全局搜索”是一个 jQuery 自动完成输入字段。它驻留在我们的 _Layout.cshtml 中,这是一个共享视图,并被其他视图多次加载。本质上,它将显示搜索关键字的自动建议列表。我们的关键字建议列表大约有 6000 项。

我们的 HomeController 如下所示:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Home()
    {
        ViewBag.Message = " Home Page.";

        GlobalSearchController controller = new GlobalSearchController();
        //_Layout.cshtml uses this for the auto-complete jQuery list
        var suggestions = controller.GetGlobalSearchSuggestions(); 
        return View(suggestions);
    }

    public ActionResult SearchResults()
    {
        ViewBag.Message = "Search Results page.";

        GlobalSearchController controller = new GlobalSearchController();
        var searchKeyword = "technology";
        //SearchResults.html uses this for the search results data
        var results = controller.GetGlobalSearchResults(searchKeyword); 
        ViewBag.SearchKeyword = searchKeyword;
        return View(results);
    }
}

_Layout.cshtml 使用这个模型:

@model MyApplication.Models.GlobalSearchSuggestions

SearchResults.cshtml 使用此模型:

@model IQueryable<MyApplication.Models.GlobalSearchResult>  

当我在 _Layout.cshtml 中使用 @model 声明时,我的问题就开始了。

我收到这样的错误:

Message = "传入字典的模型项是类型 'System.Web.Mvc.HandleErrorInfo',但是这个字典需要一个模型 'MyApplication.Models.GlobalSearchSuggestions' 类型的项目。"

如果我删除 _Layout.cshtml 的模型声明,并通过另一种方式(如 AJAX)检索“建议”,它将允许 SearchResults.cshtml 工作。不会产生错误。但我更愿意使用模型而不是 AJAX。因此,如果我将模型声明性保留在 _Layout.cshtml 中,则会出现异常。

我也无法从除主页以外的任何视图加载“建议”。这是为什么?如果我转到应用程序中的另一个视图,并尝试从我们的 _Layout.cshtml 小部件执行“全局搜索”,我不会在 jQuery 自动完成中获得任何“建议”或数据。为什么它只适用于 Home 视图和 Home 控制器?

如何避免此异常并同时使用 @model 声明?以及如何让 _Layout.cshtml 始终在自动完成字段中显示建议(而不仅仅是来自主页?)?

感谢任何帮助。谢谢!

【问题讨论】:

  • 这听起来像是Child Actions 的一个很好的用例。此外,模型通常不会在 _Layout 页面上声明,因为它会强制您的所有视图使用该模型,这就是您看到这些错误的原因。
  • @Jasen 感谢您的建议!我尝试使用@Html.Action("SearchSuggestions", "Home") 并添加了一个名为“SearchSuggestions”的新视图,但不幸的是,它在加载_Layout.cshtml 文件时会无限循环。知道如何从 _Layout.cshtml 中删除模型吗?以及将这个全局搜索“小部件”放在哪里?
  • 确保您的孩子操作return PartialView("view", model),这样您就不会递归地重新加载_Layout。您可以在任何视图上使用全局搜索子操作 - 如果您希望在每个页面上都使用 _Layout,则它是合适的。
  • 我也不认为您可以从解决方案中删除 AJAX。您将希望显示结果而不导致用户重新加载页面。

标签: asp.net-mvc razor asp.net-web-api asp.net-mvc-5 jquery-autocomplete


【解决方案1】:

这听起来像是 Child Actions 的一个很好的用例。

这是一个使用 AJAX 的基本示例,因此用户无需重新加载页面即可看到结果。

_Layout.cshtml

<div class="header">
    @Html.Action("SearchWidget", "GlobalSearch")
</div>

@RenderBody()

<script src="jquery.js" />
<script>
    $(".global-search-form").on("click", "button", function(e)
    {
        $.ajax({
            url: "/GlobalSearch/Search",
            method: "GET",
            data: { item: $("input[name='item']").val() }
        })
        .then(function(result)
        {
            $(".global-search-result").html(result);
        });
    });
</script>

_Search.cshtml

<div class="global-search-widget">
    <div class="globa-search-form">
        <label for="item">Search For:</label>
        <input type="text" name="item" value="" />
        <button type="button">Search</button>
    </div>
    <div class="global-search-results"></div>
</div>

_SearchResults.cshtml

@model MyNamespace.SearchResults

<div>Results</div>
<ul>
@foreach(var item in Model.Suggestions)
{
    <li>@item</li>
}
</ul>

搜索结果

public class SearchResults
{
    public List<string> Suggestions { get; set; }
}

全球搜索控制器

[HttpGet]
[ChildActionOnly]
public ActionResult SearchWidget()
{
    return PartialView("_Search");
}

[HttpGet]
public ActionResult Search(string item)
{
    SearchResults results = searchService.Find(item);
    return PartialView("_SearchResults", results);
}

我们将@model 声明保留在布局页面之外,并将其移至子操作的局部视图。此示例将搜索小部件加载到布局中,但您可以在任何您想要的视图上使用它。

为了简单起见,AJAX 由按钮触发,但您可以修改它以触发延迟的文本更改。结果也可以是 JSON 而不是部分视图——一些客户端 Type-Ahead 插件可能会将结果作为 JSON 处理。

如果您想导航到结果页面

您可以删除所有脚本并将您的小部件转换为适当的形式。

@model MyNamespace.SearchForm

@using(Html.BeginForm("Search", "GlobalSearch", FormMethod.Get, new { item = ViewBag.GlobalSearchKey })
{
    @Html.TextBoxFor(m => m.Item)
    <button type="submit">Search</button>
}

搜索模型

public class SearchForm
{
    public string Item { get; set; }
}

调整您的布局以将参数传递回搜索小部件。这将在结果页面中保留搜索键。

@Html.Action("SearchWidget", "GlobalSearch", new { item = ViewBag.GlobalSearchKey })

SearchWidget 操作现在传递一个参数来填充表单(如果提供)。

[HttpGet]
[ChildActionOnly]
public ActionResult SearchWidget(string item)
{
    var model = new SearchForm
    {
        Item = item ?? ""
    };
    return PartialView("_Search", model);
}

[HttpGet]
public ActionResult Search(SearchForm model)
{
    var results = searchService.Find(model.Item);
    ViewBag.GlobalSearchKey = model.Item;  // keep the same value for the form

    return View("SearchResults", results);  // full view with layout
}

我们使用ViewBag 作为搜索键,因此使用布局的任何操作都不必定义通用模型。

【讨论】:

  • 不是将结果放在 div "global-search-result" 中,有没有办法完全返回 SearchResults 视图?我正在尝试它,它通过但停留在主页视图上。我试图将“结果”分配给 $("#body").html 但这会引发 jQuery 警告,并且它根本不会呈现我们的主菜单(没有下拉菜单或动画)。
  • 它的目的是保持在同一个主页上。如果您想要一个完全不同的页面,则需要返回重定向。
  • 我可以重定向并传递模型吗?我只看到 URL 的选项?
  • 您可以重定向,但您需要将结果存储在 TempDataSession 中,然后在重定向操作中检索它。或者您可以直接使用模型返回完整视图。
  • 使用 RedirectToAction 的目的是什么?如果我已经在 ActionResult 方法中,为什么需要跳转到其他地方?你可以编辑你的答案来完成这个吗?我觉得这已经接近解决了:)
猜你喜欢
  • 2022-12-04
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 2011-12-18
  • 2021-03-23
  • 1970-01-01
相关资源
最近更新 更多