29

我正在尝试向ArticleRails 3 应用程序中的模型添加“标签”。

我想知道是否有一个 gem 或插件在模型中添加了“标记”功能以及视图的自动完成助手。

我找到了acts_as_taggable,但我不确定这是否是我应该使用的。有没有更新的东西?当我 googleacts_as_taggable 时,我得到了 2007 年的结果

4

3 回答 3

54

acts_as_taggable_onrails3-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。基本上,上面的代码将搜索所有标签,而不是将它们限制为“文章”标签。

于 2011-02-15T19:02:44.173 回答
6

act_as_taggable_on_steroids gem可能是您最好的选择。我发现许多标记宝石更像是一个“开始的好地方”,但需要进行大量自定义才能获得您想要的结果。

于 2011-02-08T19:46:30.547 回答
0

我最近写了一篇关于这个的博客文章;为简洁起见,我的方法允许您拥有(可选的)上下文过滤标签(例如,按模型和模型上的属性),而@Tim Santeford 的解决方案将为您提供模型的所有标签(不按字段过滤) . 以下是逐字帖。


我尝试了Tim Santeford 的解决方案,但问题出在标签结果上。在他的解决方案中,您可以通过自动完成返回所有现有标签,而不是局限于您的模型和可标记字段!所以,我想出了一个在我看来要好得多的解决方案;它可以自动扩展到您想要标记的任何模型,它很高效,最重要的是它非常简单。它使用acts-as-taggable-on gem 和select2 JavaScript 库。

安装 Acts-As-Taggable-On gem

  1. 在您的 Gemfile 中添加acts-as-taggable-on:gem 'acts-as-taggable-on', '~> 3.5'
  2. 运行bundle install安装它
  3. 生成必要的迁移:rake acts_as_taggable_on_engine:install:migrations
  4. 运行迁移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与我们在模型中指定的不同。不要问我为什么,但是作为可标记的行为需要单数 + _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 集成到您的表单中

首先,我们需要包含 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 表单输入在其值更改时转换为该字符串,并设置为隐藏字段。我们很快就会做到这一点。

idandname属性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本质上,我们使用参数nametaggable_type进行AJAX 调用context。听起来有点熟?这些是我们在表单输入中设置的数据属性。当返回结果时,我们只需给 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"

这就是您在 Rails 中使用acts-as-taggable-on 和 select2 自动完成标签所需要做的一切!

于 2017-02-06T20:56:45.810 回答