Jquery 如何向Rails中的现有模型添加带有autocomplete的标记?

Jquery 如何向Rails中的现有模型添加带有autocomplete的标记?,jquery,ruby-on-rails,autocomplete,tagging,acts-as-taggable,Jquery,Ruby On Rails,Autocomplete,Tagging,Acts As Taggable,我试图在Rails 3应用程序中向文章模型添加“标记” 我想知道是否有一个gem或插件既添加了模型中的“标记”功能,也添加了视图的自动完成帮助器 我发现可以作为标记使用,但我不确定这是否是我应该使用的。有更新的吗?我从2007年的谷歌搜索结果中发现,创业板可能是你最好的选择。我发现许多标签宝石更像是一个“良好的起点”,但随后需要大量的定制才能得到您想要的结果。并很好地协同工作,形成一个如此相似的标签系统,请参见下面的示例。我认为还没有一个适合rails的全能选项 按照以下步骤安装此所有组件: 一

我试图在Rails 3应用程序中向
文章
模型添加“标记”

我想知道是否有一个gem或插件既添加了模型中的“标记”功能,也添加了视图的自动完成帮助器

我发现
可以作为标记使用
,但我不确定这是否是我应该使用的。有更新的吗?我从2007年的谷歌搜索结果中发现,创业板可能是你最好的选择。我发现许多标签宝石更像是一个“良好的起点”,但随后需要大量的定制才能得到您想要的结果。

并很好地协同工作,形成一个如此相似的标签系统,请参见下面的示例。我认为还没有一个适合rails的全能选项

按照以下步骤安装此所有组件:

一,。备份你的rails应用程序

2.安装

注意:您可以使用
jqueryrails
安装jqueryui,但我选择不安装

三,。下载并安装

选择一个主题,这将赞美你的网页设计(一定要测试自动完成演示与你选择的主题,默认的主题不适合我)。下载自定义zip并将
[zipfile]/js/jquery ui-#.#.#.#.custom.min.js
文件放入应用程序的
/public/javascripts/
文件夹中。将
[zipfile]/css/custom-theme/
文件夹和所有文件放入应用程序的
public/stylesheets/custom-theme/
文件夹

四,。将以下内容添加到您的文件中,然后运行“bundle install”

gem'在'
gem'rails3jqueryautocomplete'

五,。从控制台运行以下命令:

rails生成作为标记的行为:迁移
rake数据库:迁移
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" %>
查看示例

class Article < ActiveRecord::Base
   acts_as_taggable_on :tags
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 %> 

', ' %> 
#注:上面的标记列表是由acts作为标记创建的虚拟列
注意:此示例假设您在整个应用程序中仅标记一个模型,并且仅使用默认标记类型:标记。基本上,上面的代码将搜索所有标记,而不是将它们限制为“Article”标记

我最近写了一篇关于这个的文章;为简洁起见,我的方法允许您使用(可选)上下文过滤标记(例如,根据模型和模型上的属性),而@Tim Santeford的解决方案将为您获取模型的所有标记(不按字段过滤)。下面是逐字记录的帖子


我试过了,但问题在于标记结果。在他的解决方案中,您可以通过autocomplete返回所有现有标记,而不局限于您的模型和可标记字段!因此,我提出了一个解决方案,在我看来,这个方案要好得多;它可以自动扩展到您想要标记的任何模型,它非常高效,最重要的是它非常简单。它使用gem和JavaScript库

在gem上安装Acts As Taggable
  • 将作为标记添加到gem文件:
    gem'作为标记添加到',“~>3.5'
  • 运行
    bundle install
    安装它
  • 生成必要的迁移:
    rake充当引擎上的标记:安装:迁移
  • 使用
    rake db:migrate
  • 完成了

    在MVC中设置普通标记 假设我们有一个
    电影
    模型(因为我有)。只需将以下两行添加到模型中:

    class Film < ActiveRecord::Base
        acts_as_taggable
        acts_as_taggable_on :genres
    end
    
    请注意,参数不是我们在模型中指定的
    genres
    。不要问我为什么,但在列表中充当标记,这是视图中需要的

    到视图层上!我使用gem和模板引擎进行查看,因此如果不使用gem,我的表单可能会与您的表单略有不同。但是,它只是一个正常的文本输入字段:

    = f.input :genre_list, input_html: {value: @film.genre_list.to_s}
    
    您需要设置该值的
    input\u html
    属性,以便将其呈现为逗号分隔的字符串(这是控制器中可标记的内容)。当您提交表单时,标签现在应该可以工作了!如果它不起作用,我建议看(神奇的)

    将select2集成到表单中 首先,我们需要包括select2库;您可以手动将其包括在内,也可以使用(我的首选项)为Rails资产管道打包select2的gem

    将gem添加到您的gem文件中:
    gem'select2rails'、“~>4.0”
    ,然后运行
    bundle安装

    在资产管道中包括JavaScript和CSS:

    在application.js中:
    /=require选择2 full
    。在application.css中:
    *=需要选择2

    现在,您需要稍微修改表单,以包含select2希望标记的内容。这看起来有点混乱,但我会解释一切。更改以前的表单输入:

    = f.input :genre_list, input_html: {value: @film.genre_list.to_s}
    
    致:

    我们添加一个隐藏的输入,它将作为发送给控制器的真实值。Select2返回一个数组,其中充当标记,需要一个逗号分隔的字符串。select2表单输入的值更改时将转换为该字符串,并设置为隐藏字段。我们很快就会谈到的

    f.input
    id
    name
    属性实际上并不重要。它们不能与您的
    隐藏的
    输入重叠。
    数据
    散列在这里非常重要。
    taggable
    字段允许我们使用JavaScript一次性初始化所有select2输入,而不是手动按id初始化每个输入。
    taggable_type
    字段用于过滤特定型号的标记,
    context
    字段用于过滤该字段中以前使用过的标记。最后是class FilmsController < ApplicationController def index ... end ... private def films_params params[:film].permit(..., :genre_list) end end
    = f.input :genre_list, input_html: {value: @film.genre_list.to_s}
    
    = 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
    
    // 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
            }
        });
    });
    
    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
    
    [
        {
            "id": 12,
            "name": "comedy",
            "taggings_count": 1
        },
        {
            "id": 11,
            "name": "conspiracy",
            "taggings_count": 1
        }
    ]
    
    /*
    * 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);
    });