如何在 Layout.cshtml 中设计 MVC5 全局搜索功能
How to design an MVC5 Global Search feature in Layout.cshtml
我正在尝试实现一个 "global search" 功能,该功能在我们的主菜单上方可用,在我们应用程序的所有视图中。它看起来像这样:
"global search" 是一个 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 = "The model item passed into the dictionary is of type
'System.Web.Mvc.HandleErrorInfo', but this dictionary requires a model
item of type 'MyApplication.Models.GlobalSearchSuggestions'."
如果我删除 _Layout.cshtml 的模型声明,并通过其他方式(如 AJAX)检索 "suggestions",它将允许 SearchResults.cshtml 工作。不会产生错误。但我更愿意使用模型而不是 AJAX。所以,如果我在 _Layout.cshtml 中保留模型声明,我会得到异常。
我也无法从主页以外的任何视图加载 "suggestions"。这是为什么?如果我转到我们应用程序中的另一个视图,并尝试从我们的 _Layout.cshtml 小部件执行 "global search",我不会在 jQuery 自动完成中获得任何 "suggestions" 或数据.为什么它只适用于主页视图和主页控制器?
如何避免此异常并同时使用 @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
声明保留在布局页面之外,并将其移动到子操作的部分视图中。此示例将搜索小部件加载到布局中,但您可以在任何视图中使用它。
为简单起见,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
作为搜索键,因此使用布局的任何操作都不必定义通用模型。
我正在尝试实现一个 "global search" 功能,该功能在我们的主菜单上方可用,在我们应用程序的所有视图中。它看起来像这样:
"global search" 是一个 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 = "The model item passed into the dictionary is of type 'System.Web.Mvc.HandleErrorInfo', but this dictionary requires a model item of type 'MyApplication.Models.GlobalSearchSuggestions'."
如果我删除 _Layout.cshtml 的模型声明,并通过其他方式(如 AJAX)检索 "suggestions",它将允许 SearchResults.cshtml 工作。不会产生错误。但我更愿意使用模型而不是 AJAX。所以,如果我在 _Layout.cshtml 中保留模型声明,我会得到异常。
我也无法从主页以外的任何视图加载 "suggestions"。这是为什么?如果我转到我们应用程序中的另一个视图,并尝试从我们的 _Layout.cshtml 小部件执行 "global search",我不会在 jQuery 自动完成中获得任何 "suggestions" 或数据.为什么它只适用于主页视图和主页控制器?
如何避免此异常并同时使用 @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
声明保留在布局页面之外,并将其移动到子操作的部分视图中。此示例将搜索小部件加载到布局中,但您可以在任何视图中使用它。
为简单起见,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
作为搜索键,因此使用布局的任何操作都不必定义通用模型。