55

我需要一些关于如何在我的 Rails 应用程序中实现jquery-ui 自动完成的帮助。

我想在用户可以输入客户姓名的文本字段中添加自动完成功能。由于可能有数百名客户,我需要从表中“远程”提取建议的自动完成值(至少这是我所理解的)。

我无法理解的要点是如何向自动完成文本框提供建议的值。我已经阅读了 jquery-ui 文档,但在这件事上我似乎有点密集。

所以我真正想要的是一个如何让它在 Rails 应用程序中工作的示例,不一定是对 javascript 构建方式的完整描述(这就是 jquery-ui 团队为我所做的 =))。

例如,如何为自动完成准备数据,以及如何将自动完成功能附加到文本框。

4

7 回答 7

150

好吧,我从来没有得到我上面问题的答案,所以我最终不得不自己弄清楚。我想我应该发布我想出的解决方案,以防有其他人想知道同样的事情。

您应该知道的第一件事是,这是我第一次使用 javascript,而我刚刚掌握了 Rails。所以无论如何,请随意编辑,评论任何你觉得我错了的地方。对或错,至少我知道它按照我想要的方式运行。

我认为展示这一点的最好方法是通过例子。所以以下是我如何让自动完成小部件在我的应用程序中工作。即使您不明白发生了什么,您也可以继续将以下代码放入您的应用程序中,然后我们可以通过示例了解每个部分的工作原理。在此之后,您应该掌握如何修改它以供您使用或折射它。


在您的铁路应用程序中包含 JQUERY UI。

下载jQuery UI的副本并将jquery-ui-1.8.2.custom.min.js放在/public/javascript目录中。还要确保你有一个 jQuery 本身的副本,并且它也在同一个文件夹中。

像这样在application.html.erb文件中包含 jQuery UI 文件和 jQuery 文件。
(您可以随意命名文件,只要它们匹配)

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

在您下载的 jQuery UI 中,您将拥有一个包含所有 CSS 数据的文件夹。名称会根据您选择的主题而有所不同,例如我选择了主题“ cupertino ”。将包含 CSS 数据的整个文件夹放入“ /public/stylesheets/ ”中。然后像这样在 application.html.erb 中包含 CSS 文件。

<%= 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: ”选项中指定。因为我们想将人名列表和他们的 ID 发送到自动完成,所以我们将以下内容作为源。

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

上面的 rails 助手将转换为字符串“ /people.json ”。您不需要在“ /people.json ”创建页面。你需要做的是告诉你的 people_controller 当它收到一个 .json 格式的 /people 请求时要做什么。将以下内容放入您的 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 ”。因此,如果您要在文本字段中输入“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 的记录数量,现在我们可以将其转换为 JSON 格式以用于自动完成建议。

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

现在,只需查看“Big Chunk of Code”中的注释,它应该解释其余部分是如何联系在一起的。

最后,您的页面上应该有一个作为自动完成的文本字段和一个将参数中的 ID 发送到控制器的隐藏字段。


自定义您自己的自动完成

一旦你理解了上面的内容并想要修改它以供你使用,你应该知道从你的控制器返回的 JSON 格式如下所示:

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

在这种情况下,从 javascript 中的 JSON 字符串访问不同值的方法是:

ui.item.person.name_of_some_attribute_such_as_given_name

漂亮,简单。很像在 Rails 中访问 ActiveRecord 属性。

最后一点。我花了很多时间寻找一种不同的方式来提供隐藏值,因为我认为这个函数应该已经内置到 jquery 小部件中。然而,这种情况并非如此。在官方 jQuery 示例中清楚地表明,发送不同值然后选择作为参数的方法是使用隐藏字段。

好吧,我希望这对某人有所帮助。

戴尔

于 2010-07-11T10:07:20.400 回答
11

jQuery 1.9/1.10 移除了关键的自动完成并添加了 uiAutocomplete

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

修改到上面之后,它对我有用。

于 2014-01-04T04:59:45.750 回答
7

戴尔的答案是相当的教程。需要注意的一点是,使用您的第一个查询时,数据源将仅返回以您键入的字符串开头的匹配项。如果要在单词中的任何位置搜索,则需要更改:

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

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

%(在查询中添加了一个额外的内容)

于 2011-09-14T18:20:13.163 回答
4

我基本上遵循了 Dale 在下面的建议,但我的控制器和 js 文件略有不同 - 他的版本由于某种原因给我带来了问题(可能是 jquery 更新的 bc)

上下文:我正在尝试自动完成用户输入的 DJ 名称 - 也是一个新手

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>
于 2015-04-23T19:19:11.243 回答
2

这是一个很大的帮助。

除此之外,如果您需要获取用户图像的 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 
于 2011-06-21T07:17:02.657 回答
1

重要的是要注意,如果您的“源”相对较小,例如 50 个元素,则实现应该不同(并且更简单)。在官方文档的第四段中提到:

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

使用本地数据时,您只需获取数据并将其传递给 autocomplete 方法,它就会为您进行过滤。不需要每次输入一个term都来回访问服务器。

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();
      }
    }
  });
});
于 2015-10-16T18:54:28.677 回答
0

由于这是旧的,但谷歌仍然来到这里,关于主要答案的一个小注释,这本质上是好的,但有些事情已经改变了:

  • 查看有关 jquery 已将 .data("uiAutocomplete") 更改为 .data("autocomplete") 的答案

  • 另外我会推荐一个单独的路由资源集合只处理 json

  • 使用 rabl 创建较小的 json(或为较大的模型采摘)
  • ilike , not like, 不区分大小写
  • 前面的%,所以搜索不只是start_with。
  • 方法中的有效遍历,如 item.person.name 只是 item.name(所以删除 .person)
  • 使用咖啡(以哈姆计)
  • 使用限制,如: Person.where('given_name ilike ?', "%#{params[:term]}%").limit(20)
于 2019-10-21T18:47:51.130 回答