【发布时间】:2011-06-01 04:34:29
【问题描述】:
我正在开发一个我自己使用的博客系统,并且想实现一个自动完成标签选择(类似于 stackoverflow),我将如何实现这样的东西?任何示例或教程链接将不胜感激。
谢谢。
【问题讨论】:
标签: jquery asp.net-mvc autocomplete tags tagging
我正在开发一个我自己使用的博客系统,并且想实现一个自动完成标签选择(类似于 stackoverflow),我将如何实现这样的东西?任何示例或教程链接将不胜感激。
谢谢。
【问题讨论】:
标签: jquery asp.net-mvc autocomplete tags tagging
我使用 jQuery UI 的自动完成功能,但我预先加载了数据;
查看:
@Html.TextBoxFor(Model => Model.Tags, new { @class = "txtbox-long" })
@Html.Resource(@<link href="@Url.Content("~/Content/CSS/flick/jquery-ui-1.8.11.css")" rel="stylesheet" type="text/css" />, "css")
@Html.Resource(@<script src="@Url.Content("~/Content/JS/jquery-ui-1.8.11.min.js")" type="text/javascript" language="javascript"></script>, "js")
@Html.Resource(
@<script type="text/javascript" language="javascript">
$(document).ready(function () {
var tags; $.getJSON("/Thread/GetTags", function (data) { tags = data; });
function split(val) { return val.split(/ \s*/); }
function extractLast(term) { return split(term).pop(); }
$("#Tags")
// don't navigate away from the field on tab when selecting an item
.bind("keydown", function (event) {
if (event.keyCode === $.ui.keyCode.TAB && $(this).data("autocomplete").menu.active) event.preventDefault();
})
.autocomplete({
delay: 0,
minLength: 0,
source: function (request, response) {
response($.ui.autocomplete.filter(tags, extractLast(request.term)));
},
focus: function () {
// prevent value inserted on focus
return false;
},
select: function (event, ui) {
var terms = split(this.value);
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the space at the end
terms.push("");
this.value = terms.join(" ");
return false;
}
});
});
</script>
, "js")
控制器:
[Classes.Attributes.Ajax]
public JsonResult GetTags()
{
return Json(
TagService.GetTags().Select(x => x.Name),
"text/plain",
JsonRequestBehavior.AllowGet
);
}
工作得很好,因为它使用客户端搜索,所以可以保存对数据库的多次调用。我在一个小项目中使用它,所以不会有那么多标签。
【讨论】:
我决定尝试一下 jQuery UI 自动完成功能,它似乎很简单 :) 下面是 javascript 代码:
$(document).ready(function () {
function split(val) {
return val.split(/,\s*/);
}
function extractLast(term) {
return split(term).pop();
}
$("#TagsString")
// don't navigate away from the field on tab when selecting an item
.bind("keydown", function (event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).data("autocomplete").menu.active) {
event.preventDefault();
}
})
.autocomplete({
source: function (request, response) {
$.get("/Blog/GetTags", { term: extractLast(request.term) }, function (data) {
response($.map(data.tags, function (item) {
return {
label: item.Name,
value: item.Id
}
}))
}, "json");
},
minLength: 2,
dataType: 'json',
focus: function () {
// prevent value inserted on focus
return false;
},
select: function (event, ui) {
var terms = split(this.value);
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.label);
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join(", ");
return false;
}
});
});
这是 HTML:
<p>
@Html.TextBoxFor(Model => Model.TagsString, new { @tabindex = "2", @size = "22", @value = "", @class = "text_input" })
<label for="TagsString">
<strong class="leftSpace">Tags</strong></label></p>
<style>
.ui-autocomplete-loading
{
background: white url('/Content/Images/ui-anim_basic_16x16.gif') right center no-repeat;
}
</style>
下面是动作:
[HttpGet]
public virtual JsonResult GetTags(string term)
{
var getTags = _tag.All().Where(t => t.Name.ToLower().Contains(term.ToLower())).OrderBy(t => t.Name).ToList();
TagViewModel model = new TagViewModel()
{
Tags = Mapper.Map<List<Tag>, List<TagModel>>(getTags)
};
return Json(new
{
tags = model.Tags
}, JsonRequestBehavior.AllowGet);
}
效果很好:)
【讨论】:
在这里查看我的问题Jquery, Autocomplete using json, id's vs display values
我们实际上是“借用”(读取复制并粘贴)SO 的自动完成 javascript,然后对其进行了微调 - 例如重命名它,使其不会干扰 jquery ui's autocomplete。
两者实际上非常相似,但我们特别希望有一个像 SO 这样的标签系统。
你可以把我用过的代码从http://pastebin.com/t29RCCZg拉下来
这是我们用于标签的示例操作
public ActionResult ProfileTags(string prefix, int? limit)
{
if (!limit.HasValue)
limit = ConfigurationHelper.Paging.TagList;
if (String.IsNullOrEmpty(prefix))
prefix = String.Empty;
ProfileTagModel model = new ProfileTagModel()
{
Tags = profileTagRepository.GetList(new ProfileTagsByPrefixQuery(prefix)).OrderBy(x => x.Name).Take<ProfileTag>(limit.Value)
};
return View(model);
}
视图看起来像这样
<%@ Page Language="C#" ContentType="text/html" Inherits="System.Web.Mvc.ViewPage<ProfileTagModel>" %>
<% if(Model.Tags != null) { %>
<% foreach (ProfileTag tag in Model.Tags) { %>
<%= tag.Name + ((tag.ProfileCount > 0) ? " (" + tag.ProfileCount.ToString() + ")" : String.Empty) %>
<% } %>
<% } %>
然后我们在页面上的使用看起来是这样的
$().ready(function () {
$("#ProfileTags").troppinautocomplete('<%= Url.Action("ProfileTags", "Filter") %>', {
max: 10,
highlightItem: true,
multiple: true,
multipleSeparator: " ",
matchContains: false,
scroll: true,
scrollHeight: 300,
dataType: "html"
});
})
不过,您不必这样做。您可以让 action 方法以 json 格式返回一个对象数组,然后通过更改自动完成的声明方式,您实际上可以将标签格式化为以图标或其他功能显示。
这是一个采用 json 格式的示例
$('#troppinSearch').troppinautocomplete(url, {
dataType: 'json',
parse: function (data) {
var rows = new Array();
if (data != null) {
for (var i = 0; i < data.length; i++) {
rows[i] = { data: data[i], value: data[i].Id, result: data[i].Title };
}
}
return rows;
},
formatItem: function (row, i, n) {
return '<table><tr><td valign="top"><img height="28" width="28" src="' + row.ImageUrl + '" /></td><td valign="top" style="padding:0px 0px 0px 6px;"><div>' + row.Title + '</div><div><small>' + row.ResultType + '</small></div></td></tr></table>';
},
formatResult: function (row, i, n) {
return row.Id;
},
width: 336,
max: 20,
highlightItem: true,
multiple: false,
matchContains: true,
scroll: true,
scrollHeight: 300
}).result(function (event, data, formatted) {
var type = data.ResultType.toLowerCase();
var id = data.Id;
if (type == "product") {
window.location.href = '/Shop/Product/' + id;
} else {
window.location.href = '/Profile/Index/' + id;
}
});
动作看起来像这样
public ActionResult Search(string contentType, string prefix, int? limit)
{
if (!limit.HasValue)
limit = ConfigurationHelper.Paging.ProfileList;
SearchResponse response = GetSearchResults(contentType, prefix);
var dropDownResults = (from r in response.Results
select new
{
Id = r.Id,
Title = r.Name,
ImageUrl = r.DefaultImage,
ResultType = r.ResultType.ToString()
}).Distinct().Take(limit.Value);
return Json(dropDownResults.ToList(), JsonRequestBehavior.AllowGet);
}
当你这样做时,你不需要视图。自动完成器接收 json 数据并神奇地完成所有事情。最后的 .Result 函数可让您设置在进行选择时发生的事件。在这种情况下,它实际上是将用户发送到另一个页面,但我们使用它在隐藏字段中设置值。
编辑
我忘记了这段代码的内置 CSS 类。这是一个示例 CSS。
.ac_results{
padding:0;
border:1px solid #4c4c4c;
background-color:#ffffff;
overflow:hidden;
z-index:99999;
text-align:left;
font-size: 14px; line-height:14px;
color: #333333;
}
.ac_highlight{
font-weight:bold;
text-decoration:underline;
background-color: #ff6600;
color: #ffffff;
}
.ac_results ul{
width:100%;
list-style-position:outside;
list-style:none;
padding:0;
margin:0;
}
.ac_results li{
margin:0;
padding:3px 6px 3px 6px;
cursor:default;
display:block;
line-height:14px;
overflow:hidden;
}
.ac_loading{
background:#fff url(/Content/images/loading.gif) right center no-repeat;
}
.ac_over{
background-color:#ff6600;
color:#ffffff;
}
【讨论】:
ProfileTagModel、ProfileTagsByPrefixQuery 和存储库中的 GetList 方法?此外,您是将该 ProfileTags 作为视图还是部分视图返回?再次感谢。
ProfileTagModel 只是我用作模型的一个类。 ProfileTagsByPrefixQuery 是一个在GetList 中用于将 linq 表达式转换为 linq to nhibernate 的类。其要点是通过对传入控制器的值进行搜索来获取您的标签。如何获取这些标签取决于您的数据模型。我的恰好是使用 linq 通过存储库模型休眠。
$("#BlogTags").troppinautocomplete('<%= Url.Action("/Blog/BlogTags", "Filter") ... 。但这对启动自动完成没有任何作用。任何想法?谢谢。
您可以调用一个操作方法,该方法将获取他们当前输入的文本并返回一个包含可能标记的填充列表的视图。
您的操作方法可能如下所示:
public ActionResult GetTags(string tag)
{
List<string> tags = // get AutoComplete data from somewhere
return View(tags);
}
您的自动完成视图可能只是:
<%@ Page Language="C#" Inherits="ViewPage<IList<string>>" %>
<ul>
<% foreach(string tag in Model) { %>
<li><%=tag %></li>
<% } %>
</ul>
如果你使用 jQuery,你可以试试:
$.ajax({
url: "Autocomplete/GetTags/" + tag,
cache: false,
success: function(html) {
$("#autocomplete").html(html);
}
});
【讨论】: