Подтвердить что ты не робот

Как добавить тегирование с автозаполнением к существующей модели в Rails?

Я пытаюсь добавить "теги" к модели Article в приложении Rails 3.

Мне интересно, есть ли драгоценный камень или плагин, который добавляет как функциональность "тегов" в модели, так и автозаполнение помощников для представлений.

Я нашел acts_as_taggable, но я не уверен, что это то, что я должен использовать. Есть что-то более новое? Я получаю результаты с 2007 года, когда я google actions_as_taggable

4b9b3361

Ответ 1

act_as_taggable_on и rails3-jquery-autocomplete работать красиво вместе, чтобы сделать SO-подобную систему мечения, см. пример ниже. Я не думаю, что все подходящие все в одном варианте существуют еще для рельсов.

Выполните следующие шаги, чтобы установить все:

1. Резервное копирование вашего рельса!

2. Установите jquery-rails

Примечание. Пользовательский интерфейс jQuery можно установить с помощью jquery-rails, но я не выбрал.

3. Загрузите и установите jQuery UI

Выберите тему, которая будет дополнять ваш веб-дизайн (обязательно проверьте демоверсию автозаполнения с выбранной вами темой, тема по умолчанию для меня не работает). Загрузите пользовательский почтовый индекс и поместите файл [zipfile]/js/jquery-ui-#.#.#.custom.min.js в папку приложения /public/javascripts/. поместите папку [zipfile]/css/custom-theme/ и все файлы в папку приложения public/stylesheets/custom-theme/.

4. Добавьте в свой Gemfile следующее, а затем запустите "bundle install"

gem 'act-as-taggable-on'
gem 'rails3-jquery-autocomplete'

5. В консоли запустите следующие команды:

рельсы генерируют act_as_taggable_on: migration
rake db: migrate
рельсы создают автозаполнение: 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" %>

Пример контроллера

EDIT: Сделаны изменения, основанные на комментариях Сета Пеллегрино.

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".

Ответ 2

acts_as_taggable_on_steroids драгоценный камень, вероятно, лучший выбор. Я обнаружил, что многие из драгоценных камней с метками являются более "хорошим местом для начала", но для этого требуется достаточное количество настроек, чтобы получить желаемый результат.

Ответ 3

Я написал сообщение об ошибке об этом недавно; для краткости мой метод позволяет вам иметь (необязательные) теги, отфильтрованные по полю (например, по модели и по атрибуту на модели), Ниже приведен стенографический пост.


Я опробовал решение Тима Сантфорда, но проблема была связана с результатами тегов. В его решении вы получаете все существующие теги, возвращаемые с помощью автозаполнения, и не привязаны к вашим моделям и помеченным полям! Итак, я придумал решение, которое, на мой взгляд, намного лучше; он автоматически расширяется до любой модели, которую вы хотите пометить, это эффективно, и, прежде всего, это очень просто. Он использует acts-as-taggable-on gem и select2 Библиотека JavaScript.

Установите Драйв Acts-As-Taggable-On

  • Добавить act-as-taggable-on в ваш 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, как мы указывали в модели. Не спрашивайте меня, почему, но action-as-taggable-on ожидает единственного + _list, и это то, что требуется в представлениях.

На уровень просмотра! Я использую SimpleForm и Slim шаблон для просмотров, поэтому моя форма может выглядеть немного иначе, чем ваша, если вы не используете драгоценный камень. Но это просто нормальное поле text input:

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

Вам нужен этот атрибут input_html с этим значением, установленным для рендеринга его как строки с разделителями-запятыми (что и подразумевается в контроллере). Пометка должна теперь работать, когда вы отправляете форму! Если это не сработает, я рекомендую посмотреть (удивительный) Ryan Bates 'Railscast эпизод с тегами.

Интеграция select2 в ваши формы

Во-первых, нам нужно включить библиотеку select2; вы можете включить его вручную или использовать (мои предпочтения) select2-rails gem, которые выбирают select2 для конвейера ресурсов Rails.

Добавьте драгоценный камень в свой 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 возвращает массив , где action-as-taggable-on ожидает строку , разделенную запятыми. Ввод формы select2 преобразуется в эту строку, когда ее значение изменяется и устанавливается в скрытое поле. Мы скоро доберемся до этого.

Атрибуты id и name для f.input фактически не имеют значения. Они просто не могут пересекаться с вашим входом hidden. Хэш data действительно важен здесь. Поле taggable позволяет нам использовать JavaScript для инициализации всех входов select2 за один раз вместо ручной инициализации по идентификатору для каждого из них. Поле 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']") просто получает каждый элемент HTML, где мы имеем taggable: true, заданный в данных. Мы просто добавили это в форму, и именно поэтому - мы хотим иметь возможность инициализировать select2 для всех полей taggable.

Остальное - это только код, связанный с AJAX. По существу, мы вызываем вызов AJAX /tags с параметрами name, taggable_type и 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, используя act-as-taggable-on и select2!