Как настроить jQuery-ui autocomplete в Rails

Мне нужна помощь в том, как реализовать jQuery-ui автозаполнение в моем приложении Rails.

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

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

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

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

6 ответов


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

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

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


ВКЛЮЧИТЬ JQUERY UI В РЕЛЬСЫ ПРИЛОЖЕНИЕ.

загрузите копию jQuery UI и место jquery-ui-1.8.2.традиция.минута.js внутри / public / javascript'. Вы можете использовать это в любом представлении, но понимаете, что я буквально взял его из существующего представления, принадлежащего контроллеру под названием "links_controller", и он извлекает данные из "people_controller". Надеюсь, вы знаете достаточно о Rails, чтобы решить, что вам нужно изменить, чтобы это сработало для вас.

-- Begin большой кусок кода --

    <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, но не волнуйтесь, если вы не знакомы с ним ... как и я =). Достаточно хорошо знать, что это способ форматирования текста так что другие части вашего / других приложений могут использовать его.

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

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

помощник rails выше переведет на строку"/человек.в JSON". Вам не нужно создавать страницу в "/человек.в JSON". Что? вам нужно сделать, это сказать вашему people_controller, что делать, когда он получает запрос для /людей с .формат 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, отправленные в текстовое поле автозаполнения. Это подводит нас к следующему пункту.


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

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

виджет автозаполнения, назначенный текстовому полю, отправит все, что вы введете в текстовое поле в качестве параметра вашему источнику:. Отправляемый параметр -"термин". Поэтому, если вы введете " Joe " в текстовое поле, мы сделаем следующее:

/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 based на том, что вводится в текстовое поле автозаполнения, теперь мы можем превратить это в формат 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.пункт.человек.name_of_some_attribute_such_as_given_name

красивый, простой. Очень похоже на доступ к атрибуту ActiveRecord в Рельсы.

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

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

Дейл


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

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

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


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

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

to

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

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


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

контекст: я пытаюсь автозаполнить имена ди-джеев, введенных пользователями-также newb

DJs Контроллер

 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>

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

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

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 

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

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

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

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();
      }
    }
  });
});