【问题标题】:asp.net mvc - implementing an "autocomplete" tags selection for a blog?asp.net mvc - 为博客实现“自动完成”标签选择?
【发布时间】:2011-06-01 04:34:29
【问题描述】:

我正在开发一个我自己使用的博客系统,并且想实现一个自动完成标签选择(类似于 stackoverflow),我将如何实现这样的东西?任何示例或教程链接将不胜感激。

谢谢。

【问题讨论】:

    标签: jquery asp.net-mvc autocomplete tags tagging


    【解决方案1】:

    我使用 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
        );
    }
    

    工作得很好,因为它使用客户端搜索,所以可以保存对数据库的多次调用。我在一个小项目中使用它,所以不会有那么多标签。

    【讨论】:

      【解决方案2】:

      我决定尝试一下 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);
              }
      

      效果很好:)

      【讨论】:

      • 是的 eth0,抱歉没有提到它 :) 应该说“...主要基于 eth0 的代码”。
      【解决方案3】:

      在这里查看我的问题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;
      
      }
      

      【讨论】:

      • 嗨乔希,我会尽快尝试你的代码,希望它会成功。关于您的代码的一个问题,它是否允许像 SO 支持的多个标签?谢谢。
      • 最上面的那个,是的。有一个名为 multiple 的选项,它设置为 true。分隔符是一个空格,就像在 SO 上一样。
      • 嗨 Josh,您能否详细说明一下这些模型和方法:ProfileTagModelProfileTagsByPrefixQuery 和存储库中的 GetList 方法?此外,您是将该 ProfileTags 作为视图还是部分视图返回?再次感谢。
      • ProfileTags 是一个完整的视图。 ProfileTagModel 只是我用作模型的一个类。 ProfileTagsByPrefixQuery 是一个在GetList 中用于将 linq 表达式转换为 linq to nhibernate 的类。其要点是通过对传入控制器的值进行搜索来获取您的标签。如何获取这些标签取决于您的数据模型。我的恰好是使用 linq 通过存储库模型休眠。
      • 我似乎无法让它工作,这是我的工作流程: 1) 将 CSS 样式复制到我的样式表中。 2)从Pastebin复制js并将其命名为tageditor.js,将其包含在页面中。在博客控制器中创建一个名为 BlogTags 的新 ActionResult,在博客视图文件夹中创建一个名为 BlogTags 的视图。在我可以创建新博客文章的主“创建”视图中,我有一个 Id = BlogTags 的输入文本框,然后我使用此脚本$("#BlogTags").troppinautocomplete('&lt;%= Url.Action("/Blog/BlogTags", "Filter") ... 。但这对启动自动完成没有任何作用。任何想法?谢谢。
      【解决方案4】:

      您可以调用一个操作方法,该方法将获取他们当前输入的文本并返回一个包含可能标记的填充列表的视图。

      您的操作方法可能如下所示:

      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);
          }
      });
      

      【讨论】:

        猜你喜欢
        • 2016-03-21
        • 1970-01-01
        • 2014-05-18
        • 1970-01-01
        • 1970-01-01
        • 2011-10-06
        • 2018-10-30
        • 1970-01-01
        • 2012-01-17
        相关资源
        最近更新 更多