如何在 Rails 中为现有模型添加自动完成标记?
Posted
技术标签:
【中文标题】如何在 Rails 中为现有模型添加自动完成标记?【英文标题】:How to add tagging with autocomplete to an existing model in Rails? 【发布时间】:2011-06-23 15:47:31 【问题描述】:我正在尝试向 Rails 3 应用程序中的 Article
模型添加“标签”。
我想知道是否有一个 gem 或插件在模型中添加了“标记”功能以及视图的自动完成助手。
我找到了acts_as_taggable
,但我不确定这是否是我应该使用的。有没有更新的东西?当我 googleacts_as_taggable 时,我得到了 2007 年的结果
【问题讨论】:
我实施并测试了我的答案,效果很好!请参阅我的更新答案以及完整的说明和修复。 【参考方案1】:acts_as_taggable_on 和 rails3-jquery-autocomplete 可以很好地协同工作,形成一个类似 SO 的标记系统,请参见下面的示例。我认为目前还没有适合 Rails 的多合一选项。
按照以下步骤安装:
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 'rails3-jquery-autocomplete'
5 .从控制台运行以下命令:
rails 生成acts_as_taggable_on:migration 耙分贝:迁移 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 的 cmets 进行了更改。
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 %>
注意:此示例假设您只在整个应用程序中标记一个模型,并且您只使用默认标记类型 :tags。基本上,上面的代码将搜索所有标签,而不是将它们限制为“文章”标签。
【讨论】:
@deb - 通过查看源 html 确认包含 jquery 和 jquery ui、autocomplete-rails js 文件。此外,请确保还包含 ui css 文件。你是用 Firebug 来调试的吗? @deb - 太好了!我认为这个解决方案更复杂,任何一个使用者都会喜欢。但是我认为这两个插件都已经很成熟并且本身就很好。如果我将来找到更优雅的解决方案,我会尝试在这里发布。 @TimSanteford 当acts_as_taggable_on 为namespaced 时,上面的解决方案似乎失效了。更改自动完成行以显式指定标记类(例如autocomplete :tag, :name, :class_name => 'ActsAsTaggableOn::Tag'
)使所有内容重新启动并运行。
为什么第一项是“备份您的 rails 应用程序”?当然,您的 rails 应用程序已经在版本控制下,因此您不需要备份它,并且您已经对所有更改进行了版本控制?
我知道这个答案有点老了,但知道 rails3-jquery-autocomplete gem 是否可以与 jquery-ui-rails gem 一起使用 (github.com/joliss/jquery-ui-rails)?【参考方案2】:
acts_as_taggable_on_steroids gem 可能是您最好的选择。我发现许多标记 gem 更像是一个“开始的好地方”,但需要进行大量自定义才能获得所需的结果。
【讨论】:
谢谢,我会看看可标记的类固醇。 Rails gems/plugins 的名字肯定很有趣【参考方案3】:我最近为此写了blog post;为简洁起见,我的方法允许您拥有(可选的)context 过滤标签(例如,按模型和模型上的属性),而 @Tim Santeford 的解决方案将为您提供所有标签模型(未按字段过滤)。以下是逐字帖。
我尝试了Tim Santeford's solution,但问题出在标签结果上。在他的解决方案中,您会通过自动完成功能返回所有个现有标签,而不是限制在您的模型和可标记字段中!所以,我想出了一个在我看来要好得多的解决方案;它可以自动扩展到您想要标记的任何模型,它很高效,最重要的是它非常简单。它使用 acts-as-taggable-on gem 和 select2 JavaScript 库。
安装 Acts-As-Taggable-On gem
-
将作为可标记的行为添加到您的 Gemfile:
gem 'acts-as-taggable-on', '~> 3.5'
运行bundle install
进行安装
生成必要的迁移:rake acts_as_taggable_on_engine:install:migrations
使用rake db:migrate
运行迁移
完成!
在 MVC 中设置常规标记
假设我们有一个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
。不要问我为什么,但是acts-as-taggable-on 需要 singular + _list,而这正是视图中所要求的。
到视图层!我使用SimpleForm gem 和Slim 模板引擎来查看视图,因此如果您不使用 gem,我的表单可能看起来与您的有所不同。但是,它只是一个普通的文本输入字段:
= f.input :genre_list, input_html: value: @film.genre_list.to_s
您需要设置了该值的 input_html
属性,以便将其呈现为逗号分隔的字符串(这是控制器中预期的可标记行为)。当您提交表单时,标记现在应该可以工作了!如果还不行,我推荐观看(精彩)Ryan Bates' Railscast episode on tagging。
将 select2 集成到您的表单中
首先,我们需要包含 select2 库;您可以手动包含它,也可以使用(我的偏好)select2-rails gem,它为 Rails 资产管道打包 select2。
将 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 返回一个数组,其中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 字段初始化 select2。
剩下的只是与 AJAX 相关的代码。本质上,我们使用参数name
、taggable_type
和context
对/tags
进行AJAX 调用。听起来有点熟?这些是我们在表单输入中设置的数据属性。当返回结果时,我们只需给 select2 标签的名称。
现在你可能在想:我没有/tags
路由!。你说得对!但你即将:)
添加 /tags 路由
进入您的routes.rb
文件并添加以下内容:resources :tags
。您不必为标签添加 all 路线,但我这样做是为了让我可以轻松地添加 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 自动完成标签所需要做的一切!
【讨论】:
非常有趣,我正在尝试让它工作,但标签控制器会抛出语法错误:“意外的 '.',期待 kEND .where”。为什么会这样?标记工作,但自动完成还没有!在一行上压缩它时,我得到:“参数数量错误(0 代表 1)” @SEJU 您实际上可能有语法错误。你能把你的代码贴在Pastebin或其他东西上并链接吗? 我把它放在pastebin.com/6tnWqHVy上。由于标记工作,但自动完成没有,我怀疑标签控制器可能是问题。 @SEJU 由于您使用的是 Rails 3,因此您似乎不能使用.where.not
进行否定查询...您必须将其更改为类似于 in this answer。玩弄那个特定的语法...我从来没有使用过 Rails 3,所以我不知道如何更改它的精确示例,但是您肯定必须更改 .where.not
子句!如果你喜欢我的回答,请给我 +1 ;)
怎么样:“.where(:name != params[:tags_chosen])”,这样页面加载,但我得到一个无效的 mysql 语句。 pastebin.com/deCW0FsL以上是关于如何在 Rails 中为现有模型添加自动完成标记?的主要内容,如果未能解决你的问题,请参考以下文章