我正在尝试向Article
Rails 3 应用程序中的模型添加“标签”。
我想知道是否有一个 gem 或插件在模型中添加了“标记”功能以及视图的自动完成助手。
我找到了acts_as_taggable
,但我不确定这是否是我应该使用的。有没有更新的东西?当我 googleacts_as_taggable 时,我得到了 2007 年的结果
我正在尝试向Article
Rails 3 应用程序中的模型添加“标签”。
我想知道是否有一个 gem 或插件在模型中添加了“标记”功能以及视图的自动完成助手。
我找到了acts_as_taggable
,但我不确定这是否是我应该使用的。有没有更新的东西?当我 googleacts_as_taggable 时,我得到了 2007 年的结果
acts_as_taggable_on和rails3-jquery-autocomplete很好地协同工作,形成一个类似 SO 的标记系统,请参见下面的示例。我认为目前尚不存在适用于 rails 的多合一选项。
请按照以下步骤安装所有内容:
1. 备份您的 Rails 应用程序!
2. 安装jquery-rails
注意:您可以安装 jQuery UI,jquery-rails
但我选择不安装。
3. 下载并安装jQuery UI
选择一个与您的网页设计相得益彰的主题(请务必使用您选择的主题测试自动完成演示,默认主题对我不起作用)。下载自定义 zip 并将[zipfile]/js/jquery-ui-#.#.#.custom.min.js
文件放入应用程序的/public/javascripts/
文件夹中。将[zipfile]/css/custom-theme/
文件夹和所有文件放入应用程序的public/stylesheets/custom-theme/
文件夹中。
4. 将以下内容添加到您的 Gemfile 中,然后运行“捆绑安装”
gem '充当可标记'
gem 'rails3-jquery-autocomplete'
5. 从控制台运行以下命令:
rails generateacts_as_taggable_on:migration
rake db:migrate
rails generate autocomplete:install
在您的应用中进行这些更改
在应用程序布局中包含必要的 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
路由.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 %>
注意:此示例假设您仅在整个应用程序中标记一个模型,并且您仅使用默认标记类型 :tags。基本上,上面的代码将搜索所有标签,而不是将它们限制为“文章”标签。
act_as_taggable_on_steroids gem可能是您最好的选择。我发现许多标记宝石更像是一个“开始的好地方”,但需要进行大量自定义才能获得您想要的结果。
我最近写了一篇关于这个的博客文章;为简洁起见,我的方法允许您拥有(可选的)上下文过滤标签(例如,按模型和模型上的属性),而@Tim Santeford 的解决方案将为您提供模型的所有标签(不按字段过滤) . 以下是逐字帖。
我尝试了Tim Santeford 的解决方案,但问题出在标签结果上。在他的解决方案中,您可以通过自动完成返回所有现有标签,而不是局限于您的模型和可标记字段!所以,我想出了一个在我看来要好得多的解决方案;它可以自动扩展到您想要标记的任何模型,它很高效,最重要的是它非常简单。它使用acts-as-taggable-on gem 和select2 JavaScript 库。
gem 'acts-as-taggable-on', '~> 3.5'
bundle install
安装它rake acts_as_taggable_on_engine:install:migrations
rake db:migrate
完毕!
假设我们有一个Film
模型(因为我有)。只需将以下两行添加到您的模型中:
class Film < ActiveRecord::Base
acts_as_taggable
acts_as_taggable_on :genres
end
这就是模型。现在进入控制器。您需要接受参数中的标签列表。所以我有以下内容FilmsController
:
class FilmsController < ApplicationController
def index
...
end
...
private
def films_params
params[:film].permit(..., :genre_list)
end
end
请注意,该参数genres
与我们在模型中指定的不同。不要问我为什么,但是作为可标记的行为需要单数 + _list,这是视图中需要的。
到视图层!我将SimpleForm gem 和Slim模板引擎用于视图,因此如果您不使用 gem,我的表单可能看起来与您的有所不同。但是,它只是一个普通的文本输入字段:
= f.input :genre_list, input_html: {value: @film.genre_list.to_s}
您需要input_html
设置该值的该属性,以便将其呈现为逗号分隔的字符串(这是控制器中预期的可标记行为)。当您提交表单时,标记现在应该可以工作了!如果它不起作用,我建议观看(令人惊叹的)Ryan Bates 的 Railscast 剧集 on tagging。
首先,我们需要包含 select2 库;您可以手动包含它,也可以使用(我的偏好)为 Rails 资产管道打包 select2的select2-rails gem。
将 gem 添加到您的 Gemfile: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 返回一个array,其中 acts-as-taggable-on 需要一个逗号分隔的 string。select2 表单输入在其值更改时转换为该字符串,并设置为隐藏字段。我们很快就会做到这一点。
的id
andname
属性f.input
实际上并不重要。它们不能与您的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 元素。我们刚刚将它添加到表单中,这就是为什么 - 我们希望能够为所有可标记字段初始化select2。
其余的只是与 AJAX 相关的代码。/tags
本质上,我们使用参数name
和taggable_type
进行AJAX 调用context
。听起来有点熟?这些是我们在表单输入中设置的数据属性。当返回结果时,我们只需给 select2 标签的名称。
现在你可能在想:我没有/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"
这就是您在 Rails 中使用acts-as-taggable-on 和 select2 自动完成标签所需要做的一切!