52

J'ai besoin d'aide pour implémenter un jquery-ui autocomplete dans mon application Rails.Comment configurer la saisie semi-automatique jquery-ui dans Rails

Je souhaite ajouter la saisie semi-automatique dans un champ de texte dans lequel l'utilisateur peut saisir un nom de client. Comme il peut y avoir des centaines de clients, je devrai tirer les valeurs d'auto-complétion suggérées 'à distance', comme dans, à partir d'une table (au moins c'est ce que je comprends).

Le point principal que je n'arrive pas à comprendre est de savoir comment fournir les valeurs suggérées à la zone de texte d'auto-complétion. J'ai lu les docs de jquery-ui, mais je semble être un peu dense sur ce sujet. Donc, ce que je suis réellement après est un exemple de comment je peux obtenir cela pour travailler dans une application Rails, pas nécessairement une description complète de la façon dont le javascript est construit (c'est ce que l'équipe jquery-ui a fait pour moi =)). Par exemple, comment préparer les données pour l'auto-complétion et comment associer la fonctionnalité d'auto-complétion à une zone de texte.

Répondre

145

Eh bien, je n'ai jamais eu de réponse à ma question ci-dessus, j'ai donc dû me débrouiller seul. J'ai pensé que je devrais poster la solution que j'ai trouvée au cas où il y aurait d'autres gars qui se demandent la même chose.

La première chose que vous devriez savoir est que c'est ma première expérience avec javascript, et je commence juste à comprendre Rails. Donc, n'hésitez pas à modifier, commenter n'importe où vous sentez que j'ai mal tourné avec cela. À tort ou à raison, je sais que cela fonctionne comme je le voulais.

Je pense que la meilleure façon de montrer cela est par exemple. Donc, voici comment j'ai obtenu le widget autocomplete pour fonctionner dans mon application. Vous pouvez aller de l'avant et mettre le code suivant dans votre application même si vous ne comprenez pas ce qui se passe, alors nous pouvons passer en revue comment chaque partie fonctionne par l'exemple. Après cela, vous devriez avoir une idée sur la façon de le modifier pour votre utilisation ou réfracter.


INCLURE JQUERY UI DANS VOTRE RAILS APP.

Télécharger une copie de la jQuery UI et placer jquery-ui-1.8.2.custom.min.js dans votre répertoire /public/javascript. Assurez-vous également d'avoir une copie de jQuery et que celle-ci se trouve également dans le même dossier.

Incluez le fichier jQuery UI et le fichier jQuery dans votre fichier application.html.erb comme ceci.
(vous pouvez nommer les fichiers que vous plaisez aussi longtemps qu'ils correspondent)

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

Dans votre téléchargement jQuery UI, vous aurez un dossier contenant toutes vos données CSS. Le nom variera en fonction du thème que vous avez choisi, par exemple j'ai choisi le thème 'cupertino'. Placez le dossier entier contenant vos données CSS dans '/public/stylesheets/'. Ensuite, incluez le fichier CSS dans votre application.html.erb comme ceci.

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


Exemple JAVASCRIPT saisie semi-automatique

Maintenant, prenez le morceau de code suivant et placez-le dans l'une des vues de vos 'nouvelles'. Vous pouvez l'utiliser dans n'importe quelle vue, mais réalisez que je l'ai littéralement pris d'une vue existante appartenant à un contrôleur appelé 'links_controller', et qu'il tire des données d'un 'control_personnel'. J'espère que vous en savez assez sur Rails pour savoir ce que vous devez changer, donc cela fonctionne pour vous.

- Commencez gros morceau de code -

<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 %> 

- Fin Big Chunk du Code -

Bon maintenant pour relier les points.


recueillir des données pour saisie semi-automatique à utiliser comme SUGGESTIONS

Commençons par connecter des données que le champ de texte de saisie semi-automatique peut afficher dans le menu déroulant des suggestions. Le format que nous utiliserons est JSON, mais ne vous inquiétez pas si vous n'êtes pas familier avec lui ... je ne suis pas non plus =). Il suffit de savoir que c'est un moyen de mettre en forme du texte pour que d'autres parties de votre/d'autres applications puissent l'utiliser.

Les données dont le champ de texte aura besoin pour la saisie semi-automatique sont spécifiées dans l'option 'source:'. Parce que nous voulons envoyer une liste de noms de personnes et leur ID à la saisie semi-automatique, nous allons mettre ce qui suit comme source.

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

Les rails helper ci-dessus se traduira par une chaîne "/people.json". Vous n'avez pas besoin de créer une page à "/people.json". Ce que vous devez faire est de dire à votre controleur_personnel ce qu'il doit faire lorsqu'il reçoit une requête pour/people avec le format .json. Il faut mettre dans votre 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 

Maintenant, nous avons tous les gens @people être envoyés au textfield autocomplete. Cela amène le point suivant.


DONNÉES filtre utilisé pour SUGGESTION, saisie semi-automatique en fonction des commentaires

Comment le textfield autocomplete savent comment filtrer les résultats en fonction de ce que vous tapez?

Le widget de saisie semi-automatique affecté au champ de texte enverra tout ce que vous tapez dans le champ de texte en tant que paramètre à votre source :. Le paramètre envoyé est "terme".Donc, si vous tapez « Joe » dans le champ de texte, nous rendrions les éléments suivants:

/people.json?term=joe 

Voilà pourquoi nous avons ce qui suit dans le contrôleur:

# 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 

Maintenant que nous avons limité le nombre d'enregistrements attribués à @people en fonction de ce qui est tapé dans le champ de saisie semi-automatique, nous pouvons maintenant le convertir en format JSON pour les suggestions de saisie semi-automatique.

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

maintenant, juste en revue les commentaires à l'intérieur du « Big Chunk of Code » qui devrait expliquer le reste de la façon dont ce lien ensemble. À la fin, vous devriez avoir un champ de texte sur votre page qui agit comme autocomplete et un champ caché qui enverra l'ID dans un paramètre à votre contrôleur.


adaptez votre propre saisie semi-automatique

Une fois que vous comprenez ce qui précède et que vous souhaitez le modifier pour votre utilisation, vous devez savoir que le format JSON retour de votre contrôleur ressemble à ceci:

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

la façon d'accéder aux différentes valeurs de la chaîne JSON dans votre javascript dans ce cas serait:

ui.i tem.person.name_of_some_attribute_such_as_given_name

Jolie, simple. Un peu comme accéder à un attribut ActiveRecord dans Rails.

Une dernière note. J'ai passé beaucoup de temps à chercher une autre façon de fournir la valeur cachée, car je pensais que cette fonction aurait dû être intégrée au widget jquery. Cependant, ce n'est pas le cas. Il est clairement montré dans l'exemple officiel de jQuery que la manière d'envoyer une valeur différente puis sélectionnée comme paramètre est d'utiliser un champ caché.

Eh bien, j'espère que cela aide quelqu'un.

Dale

+2

Merci. C'était une aide précieuse. – rangalo

+2

Belle explication. Il y a beaucoup de trucs et astuces fragmentaires autour de l'endroit, mais je n'avais pas trouvé un exemple concis, de bout en bout jusqu'à présent. Merci pour l'aide ... – Nuby

+3

@paz vous devriez avoir votre propre blog si vous n'avez pas déjà :) très gentil, juste ce que je cherchais. – jn29098

2

Ceci est d'une grande aide. En plus de cela dans le cas où vous avez besoin de chercher l'URL de l'image de l'utilisateur, il peut ne pas être possible avec to_json. Pour cela, ajoutez le code suivant dans le modèle.

def avatar_url 
    avatar.url(:thumb) 
end 

Et puis dans le contrôleur au lieu de to_json utilisation as_json

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

Dale's Answer est tout à fait le tutoriel. Une chose à noter est que, en utilisant votre première requête, la source de données ne retournera que les correspondances commençant avec la chaîne que vous tapez.Si vous voulez rechercher sur le mot, vous devez changer:

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

à

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

(ajouté un % supplémentaire à la requête)

10

jQuery 1.9/1.10 retiré de la saisie semi-automatique clé et ajouté uiAutocomplete

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

Après modification pour abov e, ça a marché pour moi.

+0

Cela devrait être la meilleure réponse. –

4

Je essentiellement suivi les conseils de Dale ci-dessous, mais mon contrôleur et les fichiers js ont été diff- légèrement sa version a été me donne des problèmes pour une raison quelconque (peut-être bc des mises à jour jquery)

Contexte: Je suis en train de compléter automatiquement les noms de DJs tapé par les utilisateurs - aussi un newb

DJ Controller

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 

fichier 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> 
1

Il est important de noter que si votre 'source' est relativement petite, par exemple 50 éléments, l'implémentation devrait être différente (et beaucoup plus simple). Il est mentionné dans le quatrième paragraphe du document officiel:

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

Lorsque vous utilisez des données locales tout ce que vous devez faire est d'obtenir les données et les transmettre à la méthode de saisie semi-automatique, et il fera le filtrage pour vous . Vous n'avez pas besoin d'aller et venir sur le serveur chaque fois qu'un terme est entré.

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