IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Ruby Discussion :

[RAILS 3] Déclencher une modification en base sur l'action sur une check_box


Sujet :

Ruby

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 57
    Par défaut [RAILS 3] Déclencher une modification en base sur l'action sur une check_box
    Bonjour à tous,

    Je débute sur rails et le développement web de manière générale, et me trouve confronté à un petit problème sans trop savoir quoi faire exactement.

    Voilà : j'ai en base une entité Recommendation avec un attribut :content (String) et un attribut :displayed(Boolean).
    J'affiche une liste de recommendations en utilisant le partial suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    <tr>
        <td class="recommendations">
            <span class="content">
                <%= wrap(recommendation.content)%>
            </span>
            <span class="timestamp">
                Posted <%=time_ago_in_words(recommendation.created_at)%> ago.
            </span>
        </td>
        <td>
            <%= form_for Recommendation.find(recommendation) do |f| %>
                <label>Display</label><%= f.check_box :displayed %>
            <% end %>
        </td>
    </tr>
    Maintenant ce que je voudrais c'est déclencher un update de l'entité en base dès que la check_box est cochée (:displayed => true) ou décochée (:displayed => false). Mais je ne vois pas comment faire.

    J'ai vu des personnes conseiller d'utiliser le helper observe_field mais je ne comprends pas comment l'utiliser avec la check_box de la façon dont elle est créée.

    D'avance, merci pour votre aide.

  2. #2
    Membre éclairé Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Par défaut
    D'abord, j'aurais déplacé la ligne Recomandation.find... dans le controller pour garder le modèle MVC sain, - la view n'a rien a voir avec les queries et les choses pareilles. Et dans ta form tu aurais qch de genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    <%= form_for @rec do |f|%>
    ...
    <%end%>
    Je l'avais fait comme ça dans un des projets:
    dans la view:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <td>
    <%= check_box_tag 'rec[displayed]', "1", @rec.displayed, :onclick => toggle_value(@rec) %>
    <%= image_tag 'spinner.gif', :id => "spinner-#{@rec.id}",:style => 'display: none' %>
    </td>
    En suite définit la méthode 'toggle_value' dans un Helper (moi, je l'avais mis dans ApplicationHelper)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    def toggle_value(object)
        remote_function(:url => recomandation_url(:id => object),
          :method   => :put,
          :before => "$('#spinner-#{object.id}').show()",
          :after => "$('#spinner-#{object.id}').hide()",
          :with     => "this.name + '=' + this.checked")
      end
    Il ne reste qu'à mettre l'image 'spinner.gif' (voir le fichier ci-attaché) dans ton_app/public/images et c'est tout.
    Fichiers attachés Fichiers attachés

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 57
    Par défaut
    Merci pour cette réponse précise.

    Concernant :

    D'abord, j'aurais déplacé la ligne Recomandation.find... dans le controller pour garder le modèle MVC sain, - la view n'a rien a voir avec les queries et les choses pareilles. Et dans ta form tu aurais qch de genre:
    Code :
    <%= form_for @rec do |f|%>
    ...
    <%end%>
    J'utilise le renderer suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <%= render @recommendations %>
    pour afficher la liste de recommendations en utilisant le partial "_recommendation.html.erb". Et dans celui-ci la variable à utiliser est "recommendation". Du coup je suis obligé d'utiliser la méthode find(:id) pour retourner une instance à passer à mon form. Je devrais alors créer une méthode dans le application_helper qui me retourne le résultat de Recommendation.find(:id)...

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 57
    Par défaut
    Ok, j'y suis presque maintenant avec cependant un dernier problème.

    J'ai gardé le check_box_tag comme ceci pour correspondre à mes besoins :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <td>
        <label>Display</label>
        <%= check_box_tag 'recommendation[displayed]', true, recommendation.displayed, {:onclick =>updated_displayed(recommendation)} %>
    </td>
    Dans le recommendations_helper j'ai définis la méthode suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    def updated_displayed(recommendation)
        remote_function(  :url => { :action => 'update_displayed', 
                                    :controller => 'recommendations', 
                                    :id => recommendation.id}, 
                          :with => "'displayed=' + this.checked")
    end
    Afin d'atteindre la méthode 'update_displayed' du controller il faut ensuite déclarer la route suivante dans le config/routes.rb :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    post 'recommendations/update_displayed/:id', :to => "recommendations#update_displayed"
    Et la méthode du controller :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    def update_displayed
        @recommendation.displayed = params[:displayed]
        unless @recommendation.save
          flash[:error] = "Error displaying the recommendation."
        end
        redirect_back_or current_user 
    end
    Voilà ça fonctionne, mais uniquement lorsque l'on coche la check_box.
    Si on la décoche l'update en base n'est pas effectué.

    Voici les logs :

    Tout d'abord si on coche :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    Processing by RecommendationsController#update_displayed as JS
    Parameters: {"authenticity_token"=>"2m7RqiFRVBoNAnOKSzx6m2z6Iof7ZCUkcp+xorhj+HU=", "displayed"=>"true", "id"=>"39"}
    Recommendation Load (0.2ms)  SELECT "recommendations".* FROM "recommendations" WHERE ("recommendations"."id" = 39) ORDER BY recommendations.created_at DESC LIMIT 1
    User Load (0.2ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 1) LIMIT 1
    CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 1) LIMIT 1
    AREL (0.4ms)  UPDATE "recommendations" SET "updated_at" = '2010-12-15 22:18:37.673495', "displayed" = 't' WHERE ("recommendations"."id" = 39)
    On voit bien l'appel à la méthode UPDATE.

    Maintenant si on décoche :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Processing by RecommendationsController#update_displayed as JS
    Parameters: {"authenticity_token"=>"2m7RqiFRVBoNAnOKSzx6m2z6Iof7ZCUkcp+xorhj+HU=", "displayed"=>"false", "id"=>"39"}
    Recommendation Load (0.2ms)  SELECT "recommendations".* FROM "recommendations" WHERE ("recommendations"."id" = 39) ORDER BY recommendations.created_at DESC LIMIT 1
    User Load (0.2ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 1) LIMIT 1
    CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 1) LIMIT 1
    Plus de méthode UPDATE.

    Des idées pourquoi et comment corriger ce comportement ? Encore merci.

  5. #5
    Membre chevronné

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    510
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 510
    Par défaut
    Salut,
    Des idées pourquoi et comment corriger ce comportement ?
    En fait c'est le comportement normal des check_box, lorsqu'on coche, c'est bon, ça passe, mais quand on décoche la dernière case, Rails ne le voit pas.
    Au départ, c'est pas prévu d'utiliser des check_box pour faire du js ou de l'ajax, directement dessus. Il faut utiliser un submit_to_remote, qui va envoyer ta série de coches, et encore je ne suis même pas sûr que ça fonctionne, lorsque toutes les cases sont vides.
    Perso, les checkbox j'ai laissé tomber, puisque ça correspond pas au besoin, par contre je sais que certains rajoutent une checkbox cochée, invisible, pour forcer Rails à agir...idée à suivre...si vraiment il faut utiliser check_box.

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 57
    Par défaut
    Alors en suivant tous ces conseils j'ai laissé tombé l'utilisation des check_box et je suis passé à un link_to avec un image_tag d'une case cochée/décochée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    <% if (recommendation.displayed?) %>
        <%= link_to image_tag("checked.png", :border=>0), :action => :hide, :id => recommendation %>
    <% else %>
        <%= link_to image_tag("unchecked.png", :border=>0), :action => :display, :id => recommendation %>
    <%end%>
    Dans le controller :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
      def display
        @recommendation.displayed = true
        unless @recommendation.save
          flash[:error] = "Error displaying the recommendation."
        end
        redirect_back_or current_user 
      end
     
      def hide
        @recommendation.displayed = false
        unless @recommendation.save
          flash[:error] = "Error hiding the recommendation."
        end
        redirect_back_or current_user 
      end
    Et j'obtiens un comportement similaire : l'appel à la méthode :display se fait sans problème et génère un UPDATE sur la base. Alors que l'appel à la méthode :hide affiche le flash[:error] et les logs ne présentent aucun appel à la méthode UPDATE.

    J'avoue ne pas comprendre...

  7. #7
    Membre chevronné

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    510
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 510
    Par défaut
    Bizarre en effet.
    Et ça te donne quoi les logs là ?
    Tu as fait les routes pour tes 2 actions ("display" et "hide") ?

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 57
    Par défaut
    J'ai effectivement ajouté les routes suivantes pour les méthodes hide et display :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    get 'recommendations/display/:id', :to => "recommendations#display"
    get 'recommendations/hide/:id', :to => "recommendations#hide"
    Voici les logs :

    Pour l'action de coche
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    Started GET "/recommendations/display/33" for 127.0.0.1 at Sat Dec 18 10:46:33 +0100 2010
      Processing by RecommendationsController#display as HTML
      Parameters: {"id"=>"33"}
      Recommendation Load (0.2ms)  SELECT "recommendations".* FROM "recommendations" WHERE ("recommendations"."id" = 33) ORDER BY recommendations.created_at DESC LIMIT 1
      User Load (0.2ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 1) LIMIT 1
      CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 1) LIMIT 1
      AREL (0.7ms)  UPDATE "recommendations" SET "displayed" = 't', "updated_at" = '2010-12-18 09:46:33.499891' WHERE ("recommendations"."id" = 33)
    Redirected to http://localhost:3000/users/1
    Completed 302 Found in 40ms

    On a bien l'appel à la méthode UPDATE.

    Et pour l'action de décoche :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Started GET "/recommendations/hide/45" for 127.0.0.1 at Sat Dec 18 10:52:22 +0100 2010
      Processing by RecommendationsController#hide as HTML
      Parameters: {"id"=>"45"}
      Recommendation Load (0.2ms)  SELECT "recommendations".* FROM "recommendations" WHERE ("recommendations"."id" = 45) ORDER BY recommendations.created_at DESC LIMIT 1
      User Load (0.2ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 1) LIMIT 1
      CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = 1) LIMIT 1
    Redirected to http://localhost:3000/users/1
    Completed 302 Found in 37ms
    Pas de méthode UPDATE.

    Dans le controller, la méthode hide :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
      def hide
        @recommendation.displayed = false
        unless @recommendation.save
          flash[:error] = "Error hiding the recommendation."
        end
        redirect_back_or current_user 
      end
    Et le model Recommendation est le suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    class Recommendation < ActiveRecord::Base
      attr_accessible :content, :displayed
     
      belongs_to :user
     
      validates :content,  :presence => true,
                        :length => {:maximum => 2000}
      validates :user_id, :presence => true
     
      validates :displayed, :presence => true
     
      default_scope :order => "recommendations.created_at DESC"
     
      def displayed?
        return displayed
      end
     
    end
    Comme la méthode save ne se passe pas bien puisque le message flash[:error] est affiché sur ma page, je soupçonne un mauvais paramétrage de mon modèle... Enfin j'en sais trop rien pour l'instant en fait...

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 57
    Par défaut
    Ok bon tout en écrivant le post juste avant je me suis dis que c'était peut-être la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    validates :displayed, :presence => true
    dans le model qui posait problème.

    Je l'ai commentée pour tester et maintenant tout fonctionne correctement.
    Effectivement c'est logique avec ça de ne pas pouvoir mettre l'attribut à faux.

    Bien, problème résolu en tout cas.
    Merci pour l'explication concernant le comportement des check_box. Gardons en tête d'utiliser plutôt des link_to avec des images pour ce comportement.

  10. #10
    Membre chevronné

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    510
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 510
    Par défaut
    OK c'était donc une validation, dans le modèle, qui empêchait la mise à jour de la donnée. Tsss.
    C'est clair que ça aurait été utile d'avoir une trace dans les logs...

    Sinon pour revenir aux checkbox, il faut se rappeler que Rails est un langage de conventions, et donc dès qu'on sort des habitudes, ou qu'on détourne une fonctionnalité, on perd de la magie.
    Après ça n'empêche pas de le faire, mais c'est "tordu", par rapport à des solutions conventionnelles.

    En parlant des conventions, je me permets de te dire que j'ai trouvé ton montage pas très conventionnel, justement.
    Pourquoi créer 1 action "display" et 1 action "hide" ? (et donc 2 routes supplémentaires...)
    La convention voudrait plutôt que tu gardes uniquement l'action "update" native. C'est elle qui est censée mettre à jour la bdd. Récupère dans params les données qui doivent être mise à jour.

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 57
    Par défaut
    En parlant des conventions, je me permets de te dire que j'ai trouvé ton montage pas très conventionnel, justement.
    Pourquoi créer 1 action "display" et 1 action "hide" ? (et donc 2 routes supplémentaires...)
    La convention voudrait plutôt que tu gardes uniquement l'action "update" native. C'est elle qui est censée mettre à jour la bdd. Récupère dans params les données qui doivent être mise à jour.
    Effectivement, avec un petit refactor et une approche plus REST du problème j'ai écris ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    <% if (recommendation.displayed?) %>
    	<%= link_to image_tag("checked.png", :border=>0), recommendation_path(:id=>recommendation, :value=>false), :method => :put%>
    <% else %>
    	<%= link_to image_tag("unchecked.png", :border=>0), recommendation_path(:id=>recommendation, :value=>true), :method => :put%>
    <%end%>
    Et dans le controller je peux utiliser la méthode update comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    def update
        @recommendation.displayed = params[:value]
        unless @recommendation.save
            flash[:error] = "Error modifying the recommendation."
        end
        redirect_to(user_recommendations_path(current_user))
    end
    Du coup je n'ai maintenant plus besoin des routes vers les méthodes :hide et :display dans le config/routes.rb.

  12. #12
    Membre chevronné

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    510
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 510
    Par défaut

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Impact d'une migration de base de donnée (erp) sur une plateforme BI
    Par Aurel5639 dans le forum Approche théorique du décisionnel
    Réponses: 5
    Dernier message: 03/07/2012, 21h10
  2. Réponses: 1
    Dernier message: 08/10/2010, 16h38
  3. Réponses: 2
    Dernier message: 30/03/2010, 02h26
  4. Réponses: 3
    Dernier message: 16/02/2007, 12h35
  5. Action sur le serveur via une page web
    Par raphxyz dans le forum Général Conception Web
    Réponses: 4
    Dernier message: 26/08/2006, 18h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo