How to add tagging with autocomplete to an existing model in Rails?
我正在尝试在Rails 3应用程序中为
我想知道是否有一个gem或插件在模型中添加了"标记"功能以及视图的自动完成助手。
我找到了
时,我得到了2007年的结果
acts_as_taggable_on和rails3-jquery-autocomplete可以很好地协同工作,以创建类似于SO的标记系统,请参见下面的示例。我认为对于Rails来说,还没有合适的多合一选项。
请按照以下步骤安装所有组件:
1。备份您的Rails应用程序!
2。安装jquery-rails
注意:您可以使用
3。下载并安装jQuery UI
选择一个适合您网页设计的主题(请务必使用您选择的主题测试自动完成演示,默认主题对我而言不起作用)。下载自定义zip并将
4。将以下内容添加到您的Gemfile中,然后运行"捆绑安装"
gem 'acts-as-taggable-on'
gem 'rails3-jquery-autocomplete'
5。从控制台运行以下命令:
rails generate acts_as_taggable_on:migration
rake db:migrate
rails generate autocomplete:install
在您的应用中进行这些更改
在应用程序布局中包括必要的javascript和css文件:
1 2 | <%= stylesheet_link_tag"application","custom-theme/jquery-ui-1.8.9.custom" %> <%= javascript_include_tag :defaults,"jquery-ui-#.#.#.custom.min","autocomplete-rails" %> |
控制器示例
编辑:根据塞思·佩莱格里诺(Seth Pellegrino)的评论进行更改。
1 2 3 4 | class ArticlesController < Admin::BaseController #autocomplete :tag, :name <- Old autocomplete :tag, :name, :class_name => 'ActsAsTaggableOn::Tag' # <- New end |
模型示例
1 2 3 | class Article < ActiveRecord::Base acts_as_taggable_on :tags end |
Route.rb
1 2 3 | resources :articles do get :autocomplete_tag_name, :on => :collection end |
查看示例
1 2 3 4 | <%= form_for(@article) do |f| %> <%= f.autocomplete_field :tag_list, autocomplete_tag_name_articles_path, :"data-delimiter" => ', ' %> # note tag_list above is a virtual column created by acts_as_taggable_on <% end %> |
注意:此示例假定您仅在整个应用程序中标记一个模型,并且仅使用默认标记类型:tags。基本上,上面的代码将搜索所有标签,而不是将它们限制为" Article"标签。
acts_as_taggable_on_steroidsruby可能是您最好的选择。我发现许多标记ruby更像是"开始的好地方",但是需要大量的自定义才能获得想要的结果。
我最近写了一篇关于这个的博客文章;为了简洁起见,我的方法允许您具有(可选)上下文过滤的标签(例如,按模型和模型上的属性),而@Tim Santeford的解决方案将为您提供模型的所有标签(不按字段过滤) 。以下是逐字记录。
我尝试了蒂姆·桑特福德(Tim Santeford)的解决方案,但问题出在标签结果上。在他的解决方案中,您将通过自动完成功能返回所有现有标签,而不是将其限制在模型和可标记字段中!因此,我想出了一个解决方案,我认为它要好得多。它可以自动扩展到您要标记的任何模型,它高效,最重要的是,它非常简单。它使用行为上可标记的gem和select2 JavaScript库。
安装Acts-As-Taggable-On gem
完成!
在您的MVC中设置常规标记
假设我们有一个
1 2 3 4 | class Film < ActiveRecord::Base acts_as_taggable acts_as_taggable_on :genres end |
就是这个模型。现在到控制器上。您需要接受参数中的标签列表。所以我的
1 2 3 4 5 6 7 8 9 10 11 12 | class FilmsController < ApplicationController def index ... end ... private def films_params params[:film].permit(..., :genre_list) end end |
请注意,该参数不同于我们在模型中指定的
到视图层!我将SimpleForm gem和Slim模板引擎用于视图,因此如果您不使用gem,我的表单可能会与您的表单有些不同。但是,这只是一个普通的文本输入字段:
1 | = f.input :genre_list, input_html: {value: @film.genre_list.to_s} |
您需要将此
将select2集成到表单中
首先,我们需要包括select2库;您可以手动添加它,也可以(根据我的喜好)使用将select2打包到Rails资产管道中的select2-rails gem。
将gem添加到您的Gemfile中:
在资产管道中包含JavaScript和CSS:
在application.js中:
现在,您需要对表单进行一些修改,以包括select2期望进行标记的内容。这似乎有点令人困惑,但是我将解释所有事情。更改您以前的表单输入:
1 | = f.input :genre_list, input_html: {value: @film.genre_list.to_s} |
至:
1 2 3 4 5 6 7 | = f.hidden_field :genre_list, value: @film.genre_list.to_s = f.input :genre_list, input_html: { id:"genre_list_select2", name:"genre_list_select2", multiple: true, data: { taggable: true, taggable_type:"Film", context:"genres" } }, collection: @film.genre_list |
我们添加了一个隐藏的输入,它将作为发送给控制器的实际值。 Select2返回一个数组,其中acts-as-taggable-on需要一个逗号分隔的字符串。当select2表单输入的值更改时,会将其转换为该字符串,并将其设置为隐藏字段。我们会尽快解决。
下一部分是JavaScript。我们需要在整个应用程序中初始化所有select2元素。为此,我只需将以下函数添加到我的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | // Initialize all acts-as-taggable-on + select2 tag inputs $("*[data-taggable='true']").each(function() { console.log("Taggable:" + $(this).attr('id') +"; initializing select2"); $(this).select2({ tags: true, theme:"bootstrap", width:"100%", tokenSeparators: [','], minimumInputLength: 2, ajax: { url:"/tags", dataType: 'json', delay: 100, data: function (params) { console.log("Using AJAX to get tags..."); console.log("Tag name:" + params.term); console.log("Existing tags:" + $(this).val()); console.log("Taggable type:" + $(this).data("taggable-type")); console.log("Tag context:" + $(this).data("context")); return { name: params.term, tags_chosen: $(this).val(), taggable_type: $(this).data("taggable-type"), context: $(this).data("context"), page: params.page } }, processResults: function (data, params) { console.log("Got tags from AJAX:" + JSON.stringify(data, null, '\\t')); params.page = params.page || 1; return { results: $.map(data, function (item) { return { text: item.name, // id has to be the tag name, because acts_as_taggable expects it! id: item.name } }) }; }, cache: true } }); }); |
这可能看起来很复杂,但并不难。基本上,
其余的只是与AJAX相关的代码。本质上,我们使用参数
现在您可能在想:我没有
添加/ tags路由
进入您的
那实际上是我们目前唯一需要的路线。现在有了路线,我们必须创建一个名为
的控制器
1 2 3 4 5 6 7 8 9 10 11 12 | class TagsController < ApplicationController def index @tags = ActsAsTaggableOn::Tag .where("name ILIKE ?","%#{params[:name]}%") .where.not(name: params[:tags_chosen]) .includes(:taggings) .where(taggings: {taggable_type: params[:taggable_type]}) @tags = @tags.where(taggings: {context: params[:context] }) if params[:context] @tags.order!(name: :asc) render json: @tags end end |
这很简单。我们可以使用参数
1 2 3 4 5 6 7 8 9 10 11 12 | [ { "id": 12, "name":"comedy", "taggings_count": 1 }, { "id": 11, "name":"conspiracy", "taggings_count": 1 } ] |
现在在select2输入中,您应该将"喜剧"和"阴谋"视为自动完成的标签选项!
我的标签无法保存!
还有最后一步。我们需要将select2值设置到我们先前创建的
根据您构造表单的方式,此代码可能与您有所不同,但是您本质上想获取select2输入,将字符串数组转换为CSV字符串(例如
您可以捕获select2输入更改事件,或者其他适合您的事件。这是您的选择,但必须执行此步骤以确保Rails控制器正确接收该值。同样,在application.js中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /* * When any taggable input changes, get the value from the select2 input and * convert it to a comma-separated string. Assign this value to the nearest hidden * input, which is the input for the acts-on-taggable field. Select2 submits an array, * but acts-as-taggable-on expects a CSV string; it is why this conversion exists. */ $(document).on('select2:select select2:unselect',"*[data-taggable='true']", function() { var taggable_id = $(this).attr('id') // genre_list_select2 --> genre_list var hidden_id = taggable_id.replace("_select2",""); // film_*genre_list* ($= jQuery selectors ends with) var hidden = $("[id$=" + hidden_id +"]") // Select2 either has elements selected or it doesn't, in which case use [] var joined = ($(this).val() || []).join(","); hidden.val(joined); }); |
成功转换值后,您应该在控制器操作中看到以下内容:
这就是您需要使用acts-as-taggable-on和select2在Rails中完成自动完成标记的全部!