Как настроить автозаполнение jquery-ui в Rails

Мне нужна помощь в реализации автозаполнения jquery-ui в моем приложении Rails.

Я хочу добавить автозаполнение в текстовое поле, где пользователь может ввести имя клиента. Поскольку клиентов могут быть сотни, мне нужно будет «удаленно» вытащить предлагаемые значения автозаполнения, например, из таблицы (по крайней мере, это то, что я понимаю).

Главное, что я не понимаю, это как предоставить предлагаемые значения в текстовое поле автозаполнения. Я прочитал документы jquery-ui, но, похоже, я немного скучен по этому вопросу.

Итак, что мне действительно нужно, так это пример того, как я могу заставить это работать в приложении Rails, не обязательно полное описание того, как построен javascript (это то, что команда jquery-ui сделала для меня =)).

Например, как подготовить данные для автозаполнения и как прикрепить функцию автозаполнения к текстовому полю.


person Oscar    schedule 06.07.2010    source источник


Ответы (7)


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

Первое, что вы должны знать, это то, что это мой первый опыт работы с javascript, и я только осваиваю Rails. Так что, во что бы то ни стало, не стесняйтесь редактировать, комментировать везде, где вы чувствуете, что я ошибся с этим. Правильно это или нет, по крайней мере, я знаю, что это работает так, как я хотел.

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


ВКЛЮЧИТЕ ИНТЕРФЕЙС JQUERY В ВАШЕ ПРИЛОЖЕНИЕ RAILS.

Загрузите копию интерфейса jQuery и поместите jquery-ui-1.8.2.custom.min.js внутри вашего каталога /public/javascript. Также убедитесь, что у вас есть копия самого jQuery и что она тоже находится в той же папке.

Включите файл пользовательского интерфейса jQuery и файл jQuery в свой файл application.html.erb следующим образом.
(вы можете называть файлы как угодно, если они совпадают)

<%= javascript_include_tag 'jquery.min', 'jquery-ui-1.8.2.custom.min.js' %>

При загрузке пользовательского интерфейса jQuery у вас будет папка, содержащая все ваши данные CSS. Название будет зависеть от выбранной вами темы, например, я выбрал тему «Купертино». Поместите всю папку, содержащую ваши данные CSS, в «/public/stylesheets/». Затем включите файл CSS в ваш application.html.erb следующим образом.

<%= stylesheet_link_tag 'cupertino/jquery-ui-1.8.2.custom' %>


ПРИМЕР АВТОЗАПОЛНЕНИЯ JAVASCRIPT

Теперь возьмите следующий фрагмент кода и поместите его в одно из ваших новых представлений. Вы можете использовать это в любом представлении, но помните, что я буквально взял его из существующего представления, принадлежащего контроллеру с именем «links_controller», и он извлекает данные из «people_controller». Надеюсь, вы достаточно знаете о Rails, чтобы решить, что вам нужно изменить, чтобы это работало на вас.

-- Начать большой кусок кода --

    <script type="text/javascript">
    $(function() {

 // Below is the name of the textfield that will be autocomplete    
    $('#select_origin').autocomplete({
 // This shows the min length of charcters that must be typed before the autocomplete looks for a match.
            minLength: 2,
 // This is the source of the auocomplete suggestions. In this case a list of names from the people controller, in JSON format.
            source: '<%= people_path(:json) %>',
  // This updates the textfield when you move the updown the suggestions list, with your keyboard. In our case it will reflect the same value that you see in the suggestions which is the person.given_name.
            focus: function(event, ui) {
                $('#select_origin').val(ui.item.person.given_name);
                return false;
            },
 // Once a value in the drop down list is selected, do the following:
            select: function(event, ui) {
 // place the person.given_name value into the textfield called 'select_origin'...
                $('#select_origin').val(ui.item.person.given_name);
 // and place the person.id into the hidden textfield called 'link_origin_id'. 
        $('#link_origin_id').val(ui.item.person.id);
                return false;
            }
        })
 // The below code is straight from the jQuery example. It formats what data is displayed in the dropdown box, and can be customized.
        .data( "autocomplete" )._renderItem = function( ul, item ) {
            return $( "<li></li>" )
                .data( "item.autocomplete", item )
 // For now which just want to show the person.given_name in the list.
                .append( "<a>" + item.person.given_name + "</a>" )
                .appendTo( ul );
        };
    });
    </script>



<h1>New link</h1>

<% form_for(@link) do |f| %>
  <%= f.error_messages %>

<!-- Place the following text fields in your form, the names are not important. What is important is that they match the names in your javascript above -->
  <p>
        Select which person you want to link:<br /> 
<!-- This is the textfield that will autocomplete. What is displayed here is for the user to see but the data will not go anywhere -->
        <input id="select_origin"/>
<!-- This is the hidden textfield that will be given the Persons ID based on who is selected. This value will be sent as a parameter -->
      <input id="link_origin_id" name="link[origin_id]" type="hidden"/>
  </p>
<!-- end of notes -->
  <p>
    <%= f.label :rcvd_id %><br />
    <%= f.text_field :rcvd_id %>
  </p>
  <p>
    <%= f.label :link_type %><br />
    <%= f.text_field :link_type %>
  </p>
  <p>
    <%= f.label :summary %><br />
    <%= f.text_area :summary %>
  </p>
  <p>
    <%= f.label :active %><br />
    <%= f.check_box :active %>
  </p>
  <p>
    <%= f.submit 'Create' %>
  </p>
<% end %>

-- Конец большого фрагмента кода --

Хорошо, теперь соединим точки.


ПРЕДСТАВИТЬ ДАННЫЕ ДЛЯ АВТОЗАПОЛНЕНИЯ ДЛЯ ИСПОЛЬЗОВАНИЯ В КАЧЕСТВЕ ПРЕДЛОЖЕНИЙ

Давайте начнем с подключения некоторых данных, которые текстовое поле автозаполнения может отображать в раскрывающихся предложениях. Мы будем использовать формат JSON, но не беспокойтесь, если вы с ним не знакомы... я тоже =). Достаточно знать, что это способ форматирования текста, чтобы его могли использовать другие части ваших/других приложений.

Данные, которые потребуются текстовому полю для автозаполнения, указываются в параметре «источник:». Поскольку мы хотим отправить список имен людей и их идентификаторов в автозаполнение, мы поместим в качестве источника следующее.

source: '<%= people_path(:json) %>'  

Помощник rails выше преобразует в строку «/people.json». Вам не нужно создавать страницу в "/people.json". Что вам действительно нужно сделать, так это сообщить вашему people_controller, что делать, когда он получает запрос на /people в формате .json. Поместите следующее в свой people_controller:

def index  
# I will explain this part in a moment.
  if params[:term]
    @people = Person.find(:all,:conditions => ['given_name LIKE ?', "#{params[:term]}%"])
  else
    @people = Person.all
  end

  respond_to do |format|  
    format.html # index.html.erb  
# Here is where you can specify how to handle the request for "/people.json"
    format.json { render :json => @people.to_json }
    end
end

Теперь у нас есть все люди в @people, отправленные в текстовое поле автозаполнения. Это поднимает следующий вопрос.


ФИЛЬТР ДАННЫХ, ИСПОЛЬЗУЕМЫХ ДЛЯ АВТОЗАПОЛНЕНИЯ ПРЕДЛОЖЕНИЙ НА ОСНОВЕ ВВОДНЫХ ВВОДОВ

Как текстовое поле автозаполнения знает, как фильтровать результаты на основе того, что вы вводите?

Виджет автозаполнения, назначенный текстовому полю, будет отправлять все, что вы вводите в текстовое поле, в качестве параметра в ваш источник:. Отправляемый параметр — "term". Итак, если бы вы набрали «Джо» в текстовое поле, мы бы сделали следующее:

/people.json?term=joe

Поэтому в контроллере имеем следующее:

# If the autocomplete is used, it will send a parameter 'term', so we catch that here
    if params[:term]
# Then we limit the number of records assigned to @people, by using the term value as a filter.
      @people = Person.find(:all,:conditions => ['given_name LIKE ?', "#{params[:term]}%"])
# In my example, I still need to access all records when I first render the page, so for normal use I assign all. This has nothing to do with the autocomplete, just showing you how I used it in my situation.
    else
      @people = Person.all
    end

Теперь, когда мы ограничили количество записей, назначенных @people, на основе того, что вводится в текстовое поле автозаполнения, теперь мы можем преобразовать это в формат JSON для предложений автозаполнения.

respond_to do |format|  
      format.html # index.html.erb  
      format.json { render :json => @people.to_json }
    end 

Теперь просто просмотрите комментарии внутри «Большого куска кода», которые должны объяснить остальную часть того, как это связано.

В конце у вас должно быть текстовое поле на вашей странице, которое действует как автозаполнение, и скрытое поле, которое будет отправлять идентификатор в параметре на ваш контроллер.


НАСТРОЙТЕ АВТОЗАПОЛНЕНИЕ

Как только вы поймете вышесказанное и захотите изменить его для своего использования, вы должны знать, что формат JSON, возвращаемый вашим контроллером, выглядит следующим образом:

[{"person":{"id":1,"given_name":"joe","middle_name":"smith","family_name":"jones","nationality":"australian"}}]

Способ доступа к различным значениям из строки JSON в вашем javascript в этом случае будет следующим:

ui.item.person.name_of_some_attribute_such_as_given_name

Красиво, просто. Очень похоже на доступ к атрибуту ActiveRecord в Rails.

Последнее замечание. Я потратил много времени на поиски другого способа предоставления скрытого значения, так как думал, что эта функция должна быть встроена в виджет jquery. Тем не менее, это не так. В официальном примере jQuery ясно показано, что способ отправки другого значения, выбранного в качестве параметра, заключается в использовании скрытого поля.

Ну, я надеюсь, что это поможет кому-то.

Дол

person Oscar    schedule 11.07.2010
comment
Спасибо. Это было большим подспорьем. - person rangalo; 02.01.2011
comment
Хорошее объяснение. Там много разрозненных советов и приемов, но я до сих пор не нашел ни одного краткого, сквозного примера. Спасибо за помощь... - person Nuby; 23.01.2011
comment
@paz, у тебя должен быть свой блог, если ты еще этого не сделал :) очень мило, как раз то, что я искал. - person jn29098; 10.12.2011
comment
+1 за то, что сам разобрался и поделился богатством! Спасибо! - person ; 31.01.2012
comment
у меня сработало, но я должен изменить item.person.property_name на item.property_name, в возвращаемом json нет человека: { ... } - person simo; 19.05.2012
comment
Это отличный ответ, но вы можете взглянуть на github.com/joliss/jquery. -ui-rails, чтобы добавить библиотеки с помощью конвейера. - person Nobita; 19.07.2012
comment

jQuery 1.9/1.10 удалил автозаполнение ключа и добавил uiAutocomplete

.data("uiAutocomplete") instead of .data("autocomplete")

После изменения выше, это сработало для меня.

person Arun    schedule 04.01.2014
comment
Это должен быть лучший ответ. - person Krishna Rani Sahoo; 21.08.2014

Dale's Answer - вполне себе учебник . Следует отметить, что при первом запросе источник данных будет возвращать только совпадения beginning с введенной вами строкой. Если вы хотите искать в любом месте слова, вам нужно изменить:

@people = Person.find(:all,:conditions =>
    ['given_name LIKE ?', "#{params[:term]}%"])

to

@people = Person.find(:all,:conditions =>
    ['given_name LIKE ?', "%#{params[:term]}%"])

(добавлено % в запрос)

person piffle    schedule 14.09.2011

Я в основном следовал совету Дейла ниже, но мой контроллер и файлы js немного отличались - его версия по какой-то причине вызывала у меня проблемы (возможно, из-за обновлений jquery)

Контекст: я пытаюсь автозаполнять имена ди-джеев, введенные пользователями — тоже новичок.

DJ-контроллер

 class DjsController < ApplicationController
    def index
     if params[:term]
       @djs = Dj.is_dj.where('lower(name) LIKE ?', "%#{params[:term].downcase}%")
       respond_to do |format|  
          format.html
          format.json { render :json => @djs.map(&:name) }
       end
     end    
   end
 end

файл html.erb

  <script type="text/javascript">

$(function() {  
    $('#select_origin').autocomplete({
        source: '<%= djs_path(:json) %>'
      })

    $('.submit-comment').click(function(){
      var dj_name = $('#select_origin').val();
      $('#link_origin_id').val(dj_name);
    })

})

</script>
person strohy1210    schedule 23.04.2015

Это большая помощь.

В дополнение к этому, если вам нужно получить URL-адрес изображения пользователя, это может быть невозможно с to_json. Для этого добавьте следующий код в файл model.

def avatar_url
    avatar.url(:thumb)
end

И тогда в контроллере вместо to_json используйте as_json

respond_to do |format|
    format.json {render :json => @users.as_json(:only => [:id,:name,:username], :methods => [:avatar_url]) }
end 
person Bongs    schedule 21.06.2011

Важно отметить, что если ваш «источник» относительно небольшой, например, 50 элементов, реализация должна быть другой (и намного проще). Это упоминается в четвертом абзаце официального документа:

https://api.jqueryui.com/autocomplete/

При использовании локальных данных все, что вам нужно сделать, это получить данные и передать их методу автозаполнения, и он выполнит фильтрацию за вас. Вам не нужно возвращаться на сервер каждый раз, когда вводится термин.

function filterByTags(tags) {
  $("#stories-filter").autocomplete({
     source: tags,
     autoFocus: true
  });
}

$("#stories-filter").click(function() {
  $.ajax({
    dataType: 'json',
    method: 'GET',
    url: 'tags/index',
    data: $(this).data('project-id'),
    success: function (response) {
      if(response.success) {
        var tags = response.data.tags;
        filterByTags(tags);
      }
    },
    error: function (response) {
      if(response.status === 422) {
        var $errors = 'There are no tags in this project',
            $errorsContainer = $('.error-container');
        $errorsContainer.append($errors);
        $errorsContainer.show();
      }
    }
  });
});
person vicocas    schedule 16.10.2015

Поскольку это старо, но гугл все еще приходит сюда, небольшая заметка об основном ответе, который по сути хорош, но некоторые вещи изменились:

  • см. ответ о том, что jquery изменил .data("uiAutocomplete") на .data("autocomplete")

  • Также я бы порекомендовал отдельный маршрут для коллекции ресурсов, который просто обрабатывает json.

  • используйте rabl для создания меньшего json (или выщипывайте для больших моделей)
  • ilike , не нравится, без учета регистра
  • % впереди, так что поиск не просто start_with.
  • действительный обход в методах, таких как item.person.name, - это просто item.name (поэтому отбросьте .person)
  • использовать кофе (в ветчине)
  • использовать ограничение и где, например: Person.where('given_name ilike ?', "%#{params[:term]}%").limit(20)
person Torsten    schedule 21.10.2019