如何使用自动完成function将标记添加到Rails中的现有模型?
我正在尝试将“标签”添加到Rails 3应用程序中的Article
模型中。
我想知道是否有一个gem或插件添加了模型中的“标记”function以及视图的自动完成帮助程序。
我发现了acts_as_taggable
但我不确定这是不是我应该使用的。 有更新的东西吗? 我从2007年google acts_as_taggable获得了结果
acts_as_taggable_on和rails3-jquery-autocomplete可以很好地协同工作,制作类似SO的标记系统,请参见下面的示例。 对于rails来说,我认为还没有合适的all in one选项。
请按照以下步骤安装全部:
1。 备份你的rails应用程序!
2。 安装jquery-rails
注意:您可以使用jquery-rails
安装jQuery UI,但我选择不这样做。
3。 下载并安装jQuery UI
选择一个与您的网页设计相称的主题(请务必使用您选择的主题测试自动完成演示,默认主题对我不起作用)。 下载自定义zip并将[zipfile]/js/jquery-ui-#.#.#.custom.min.js
文件放入应用程序的/public/javascripts/
文件夹中。 将[zipfile]/css/custom-theme/
文件夹和所有文件放入应用程序的public/stylesheets/custom-theme/
文件夹中。
4。 将以下内容添加到Gemfile中,然后运行“bundle install”
gem’行为 – 可标记 – 在’
gem’rail3-jquery-autocomplete’
5。 从控制台运行以下命令:
rails生成acts_as_taggable_on:迁移
rake db:migrate
rails生成自动完成:安装
在您的应用中进行这些更改
在您的应用程序布局中包含必要的javascript和css文件:
<%= stylesheet_link_tag "application", "custom-theme/jquery-ui-1.8.9.custom" %> <%= javascript_include_tag :defaults, "jquery-ui-#.#.#.custom.min", "autocomplete-rails" %>
控制器示例
编辑:根据Seth Pellegrino的评论做出改变。
class ArticlesController < Admin::BaseController #autocomplete :tag, :name <- Old autocomplete :tag, :name, :class_name => 'ActsAsTaggableOn::Tag' # <- New end
模型示例
class Article < ActiveRecord::Base acts_as_taggable_on :tags end
Route.rb
resources :articles do get :autocomplete_tag_name, :on => :collection end
查看示例
<%= 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 %>
注意:此示例假定您仅在整个应用程序中标记一个模型,并且您只使用默认标记类型:标记。 基本上上面的代码将搜索所有标签,而不是将它们限制为“Article”标签。
acts_as_taggable_on_steroids gem可能是你最好的选择。 我发现许多标记gem更像是一个“开始的好地方”,但后来需要大量的自定义来获得你想要的结果。
我最近写了一篇关于这个的博客文章 ; 为了简洁起见,我的方法允许你有(可选的) context -filtered标签(例如模型和模型上的属性),而@Tim Santeford的解决方案将为你获取模型的所有标签(不按字段过滤) 。 以下是逐字帖。
我尝试了Tim Santeford的解决方案 ,但问题在于标签结果。 在他的解决方案中,您将获得通过自动完成返回的所有现有标签,而不是作用于您的模型和可标记字段 ! 所以,我想出了一个解决方案,在我看来要好得多; 它可以自动扩展到你想要标记的任何模型,它是高效的,最重要的是它非常简单。 它使用acts-as-taggable-on gem和select2 JavaScript库。
安装Acts-As-Taggable-On gem
- 在你的Gemfile中添加act-as-taggable-on:gem’atject
gem 'acts-as-taggable-on', '~> 3.5'
- 运行
bundle install
以安装它 - 生成必要的迁移:
rake acts_as_taggable_on_engine:install:migrations
- 使用
rake db:migrate
运行rake db:migrate
完成!
在MVC中设置正常标记
假设我们有一个Film
模型(因为我这样做)。 只需在模型中添加以下两行:
class Film < ActiveRecord::Base acts_as_taggable acts_as_taggable_on :genres end
这就是模型。 现在进入控制器。 您需要接受参数中的标记列表。 所以我在FilmsController
有以下FilmsController
:
class FilmsController < ApplicationController def index ... end ... private def films_params params[:film].permit(..., :genre_list) end end
请注意,该参数不是我们在模型中指定的genres
。 不要问我为什么,但是act-as-taggable-on需要单数+ _list ,这就是视图中所需要的。
在视图层上! 我使用SimpleForm gem和Slim模板引擎来查看视图,因此如果你不使用gem,我的表单可能看起来与你的不同。 但是,它只是一个普通的文本输入字段:
= f.input :genre_list, input_html: {value: @film.genre_list.to_s}
您需要具有该值集的input_html
属性,以便将其呈现为逗号分隔的字符串(这是控制器中期望的作为标签的行为)。 提交表单时,标签现在可以正常工作! 如果它不起作用,我建议观看(惊人的) Ryan Bates的Railscast关于标记的情节 。
将select2集成到表单中
首先,我们需要包含select2库; 你可以手动包含它,或者使用(我的偏好) select2-rails gem,它将select2包装为Rails资产管道。
将gem添加到您的Gemfile:gem'select2 gem 'select2-rails', '~> 4.0'
,然后运行bundle install
。
在资产管道中包含JavaScript和CSS:
在application.js : //= require select2-full
。 在application.css中 : *= require select2
。
现在,您需要稍微修改表单以包含select2期望的标记。 这看起来有点令人困惑,但我会解释一切。 更改以前的表单输入:
= f.input :genre_list, input_html: {value: @film.genre_list.to_s}
至:
= 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表单输入在其值更改时转换为该字符串,并设置为隐藏字段。 我们很快就会到达那里。
f.input
的id
和name
属性实际上并不重要。 它们只是不能与您hidden
输入重叠。 data
哈希在这里非常重要。 可taggable
字段允许我们使用JavaScript一次性初始化所有select2输入,而不是通过id为每个输入手动初始化。 taggable_type
字段用于过滤特定模型的标记, context
字段用于过滤之前在该字段中使用过的标记。 最后, collection
字段只是在输入中适当地设置值。
下一部分是JavaScript。 我们需要在整个应用程序中初始化所有select2元素。 为此,我只是将以下函数添加到我的application.js
文件中,以便它适用于每个路由:
// 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 } }); });
这可能看起来很复杂,但并不太难。 基本上, $("*[data-taggable='true']")
选择器只获取我们有taggable: true
每个HTML元素taggable: true
在数据中设置taggable: true
。 我们刚刚将其添加到表单中,这就是为什么 - 我们希望能够为所有可标记字段初始化select2。
其余的只是与AJAX相关的代码。 基本上,我们使用参数name
, taggable_type
和context
对/tags
进行AJAX调用。 听起来有点熟? 这些是我们在表单输入中设置的数据属性。 返回结果时,我们只需给select2标记名称。
现在你可能在想: 我没有/tags
路线! 。 你是对的! 但是你要:)
添加/ tags路由
进入routes.rb
文件并添加以下内容: resources :tags
。 您不必为标签添加所有路由,但我这样做可以让您轻松获得CRUD标记。 你也可以简单地做: get '/tags' => 'tags#index'
这真的是我们目前唯一需要的路线。 现在我们有了路由,我们必须创建一个名为TagsController
的控制器:
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
这很简单。 我们可以向/tags
发送请求,其中包含参数name
(标记文本), tags_chosen
(现有选定标记), taggable_type
(标记的模型 )和可选context
(标记的字段 )。 如果我们有“喜剧”和“阴谋”的流派标签,那么在我们的表单中键入co ,呈现的JSON应该如下所示:
[ { "id": 12, "name": "comedy", "taggings_count": 1 }, { "id": 11, "name": "conspiracy", "taggings_count": 1 } ]
现在在select2输入中,你应该看到“喜剧”和“阴谋”作为自动完成的标签选项!
我的标签不会保存!
还有最后一步。 我们需要将select2值设置为我们之前创建的hidden
字段。
根据您构建表单的方式,此代码可能会有所不同,但您基本上想要获取select2输入,将字符串数组转换为CSV字符串(例如["comedy", "conspiracy"]
- > "comedy, conspiracy"
),并将CSV字符串设置为隐藏字段。 幸运的是,这并不太难。
您可以捕获select2输入更改事件或其他适合您的事件。 这是您的选择,但必须执行此步骤以确保Rails控制器正确接收值。 再次,在application.js中 :
/* * 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); });
成功转换价值后,您应该在控制器操作中看到以下内容: "genre_list"=>"comedy,conspiracy"
这就是使用act-as-taggable-on和select2在Rails中执行自动完成标记所需的全部内容!