asp.net mvc - 如何在Layout.cshtml中设计MVC5全局搜索功能



asp.net-mvc razor (1)

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

“全局搜索”是一个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控制器?

如何避免这个异常,并使用@model声明? 我怎样才能得到_Layout.cshtml一致地显示在自动完成领域(而不仅仅是从主页?)的建议?

任何帮助表示赞赏。 谢谢!


这听起来像是一个很好的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; }
}

GlobalSearchController

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

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

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

为了简单起见,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作为搜索键,所以任何使用布局的动作都不需要定义一个通用模型。