Bonjour à tous,

Voila je me suis lancé dans le jQuery avec CakePhp, et la je bloque sur un truc.

Sur une page index, je liste l'ensemble des éléments d'un model (Compte). En haut j'ai rajouter un lien qui permet d'ajouter une opération avec le Helper Js :

<?php echo $this->Js->link('Ajouter une Opération', array(
    'controller' => 'operations',
    'action' => 'add',
    'id' => $compte'Compte']'nb']), array(
    'update' => '#modal','class'=>"sub"));
?>

Il appel donc la vue add et remplace le div id= modal dans mon layout par default.

<body>
<div class="container-fluid">
      <div class="sidebar">
          <?php echo $this->element('menu/menu'); ?>
      </div>
      <div id="content" class="content">
            <div id="modal"> 
            </div>   
        <?php echo $this->Session->flash(); ?>
        <?php echo $content_for_layout; ?>
     </div>
    </div>
  </body>
  <?php echo $this->Html->script('jquery');?>
  <?php echo $this->Html->script('jquery-ui');?>
  <?php echo $scripts_for_layout; ?>
  <?php echo $this->Js->writeBuffer(); ?>
</html>

Et voici la page appelé :

<div id="dialog" title= 'Nouvel Ope'> 
<?php echo $this->Form->create('Operation');?>
            <p class="validateTips">AAAAA</p>
            <?php
            echo $this->Form->input('name',array('label'=>"Nom : "));
            echo $this->Form->input('date',array(
                'label'=>"Date : ",
                'type'=> 'text'
                ));
            echo $this->Form->input('commentaire',array('label'=>"Commentaire : "));
            echo $this->Form->input('montant',array('label'=>"Montant : ",'type'=>'text'));
            echo $this->Form->input('pointage',array('label'=>"Pointage : "));
            echo $this->Form->input('typeoperation_id',array('label'=>"Type d'opération : "));
            echo $this->Form->input('Category',array('label'=>"Categories : ",'option' => ($categories)));
            echo $this->Form->submit('Ajouter',array('hiddenField' => true)); 
            ?>
<?php echo $this->Form->end();?>
</div>

Et le script appelé qui gère le dialog

$(function() {
        $.datepicker.setDefaults($.datepicker.regional'fr']);
        $('#OperationDate').live('click', function(){
            $(this).datepicker({
                showOn:'focus',
                dateFormat : 'yy-mm-dd',
                minDate : '-30',
                altField: "#OperationDate",
                altFormat: "dd/mm/yy",
                showOtherMonths: true,
                selectOtherMonths: true,
                showWeek: true
            }).attr("readonly","readonly").focus()
            }); 
        $( "#dialog:ui-dialog" ).dialog( "destroy" );
        var name = $("#OperationName"),
            tips = $( ".validateTips" ),
            montant = $("#OperationName"),
            allFields = $( ] ).add( name ).add( montant );
        function updateTips( t ) {
            tips
                .text( t )
                .addClass( "form-error" );
            setTimeout(function() {
                tips.removeClass( "ui-state-highlight", 1500 );
            }, 500 );
        }   
        function checkRegexp( o, regexp, n ) {
            if ( !( regexp.test( o.val() ) ) ) {
                o.addClass( "form-error" );
                updateTips( n );
                return false;
            } else {
                return true;
            }
        }
        $( "#dialog" ).dialog({
            autoOpen: false,
            height: 600,
            width: 450,
            modal: true,
            buttons: {
                "Enregistrer": function() {
                    var bValid = true;
                    allFields.removeClass( "form-error" );
                    bValid = bValid && checkRegexp( name, /^[a-z]([0-9a-z_])+$/i, "NON" );
                    bValid = bValid && checkRegexp( montant, /(+-]?\\d*\\.\\d+)(?!-+0-9\\.])+$/i, "NON" );
                    if ( bValid ) {
                        $(":submit").click();
                    }
                },
                Annuler: function() {
                    $( this ).dialog( "close" );
                }

            }
        });
        $( ".sub" )
            .button()
            .click(function(){
                $( "#dialog" ).dialog( "open" );
            });
    });

Mais voila, l'ajax est bien exécuté, dans mon layout le <div id="dialog" est bien apparu mais impossible de faire apparaitre la boite de dialogue..., je pense que cela vient du faite qu'il n'est pas existant au début.

Comment gérer ce problème ?

Merci

27 réponses


Pakito
Réponse acceptée

J'ai fini par comprendre le render() ;)

Et désolé pour le temps de réponse, je n'avais pas vu qu'il y avait 2 pages sur le sujet... -_-

alors pour la première partie, c'est parce que ton return false; est dans la fonction, et pas dans l'événement. Il faudrait faire :

$(document).ready(function() {
    $('#Users-signup').live('submit', function(){
login_ajax($(this).attr('action'));
return false;
})

C'est un événement que tu cherches à bloquer (en l'occurence, le submit) et pas une fonction.

Ensuite, pour le serialize, c'est parce que tu l'appelles depuis ta fonction, et pas depuis l’évènement. Du coup, le this n'est pas établi, puisqu'il se réfère à l'élément du DOM qui a subit l’évènement.

Ce que je fais, c'est que dans ma première requête, j'insère la vue à l'endroit voulu, et cette vue contient un bouton submit qui une fois cliqué lance le second évènement ajax-login.

Salut,

Tu parles que tu gères le modal avec ton script, mais à aucun momemnt je vois #modal dedans, pourrais-tu en dire plus, du genre comment tu ajoutes ton modal, à quel moment dans quel autre div, là c'est flou.

A+

XciD
Auteur

Salut,
J'ai modifié mon premiers post pour que le code posté soit plus claire. Si c'est toujours mal expliqué :s je retenterais le coups :)

Je ne suis pas certain d'avoir bien cerné le problème.

En gros, dans ta vue add.ctp, tu as un div#dialog. Et tu veux faire passer ce contenu à #modal.
Théoriquement, tout cela devrait être fait vu que c'est ce qui est spécifié dans update.

Tu parles de "boite de dialogue" qui n’apparaît pas, mais à quoi ça correspond concrètement ?
J'utilise une fonction similaire avec les formulaires d'inscription et de connexion qui sont appelé en Ajax dans un modal (bootstrap Twitter), et je n'ai eu aucun soucis. Donc si je peux aider, n'hésites pas ;)

XciD
Auteur

C'est exact,
Mon ajax fonctionne car le formulaire apparaît bien Cependant il napparait pas dans le modal( jquery ui)
Ta préposition est intéressante vue que c'est ce que je cherche
Si tu peux m'envoyer ton code :p
Merci !!!

A vrai dire, je n'utilise pas le HelperJs, principalement parce qu'il ne couvre pas tous mes besoins (par exemple, observeField ne s'applique qu'à change, et pas à onkeyup...).

Cela étant, je pense que le problème pourrait se situer dans les options que tu passe à ton link. Si on se fie à la doc, 'class' fait partie des options possibles pour un link via le helper Html, mais pas pour le Js. Essaie en le virant, et si le problème persiste, je te passerai mon code, même s'il diffère dans l'appel du lien.

Edit : en fait si, je viens de voir que le helper Js prend en paramètre les options du helper Html. Du coup je vais rapatrier mon code.

Je créé mon lien dans le menu (extrait de menu.ctp) :

<li>
        <?php echo $this->Html->link('Inscription', array('controller' => 'users', 'action' => 'enregistrer'), array('class' => 'modal-link')); ?>
    </li>

Je le gère en js :

$('.modal-link').click(function(){
        $('.modal').remove();
        var url = $(this).attr('href');

        $.ajax({
            url: url,
            async:true,
            type:'post',
            complete: function(request) {
                $('#corps').append('<div class="modal-backdrop fade in"></div>');
                $('#corps').append('<div class="modal"></div>');
                $('.modal, .modal-backdrop').hide();
                $('.modal').append(request.responseText);
                $('.modal').draggable();
                $('.modal-backdrop').show();
                $('.modal').fadeIn('200');
            }
        });

        return false;
    });

Donc en gros, s'il existe déjà un div.modal, je le supprime, et je récupère l'url appelée par le lien, c'est elle qui servira à spécifier la destination de la requête ajax.
Une fois la requête effectuée, j'ajoue à mon #corps un les 2 divs de mon modal (l'overlay puis le modal en lui même qui accueillera les données, et je les cache tant les données ne sont pas insérées.
J'insère les données dedans, je rends le modal déplaçable puis j'affiche le tout.

La vue appelée est toute simple (enregistrer-ajax.ctp) :

<div class="modal-header">
    <a href="#" class="close">×</a>
    <h3>Inscription</h3>
</div>
<?php echo $this->Form->create('User', array('id' => 'Users-inscription')); ?>
<div class="modal-body">
    <?php echo $this->Form->input('username', array('label' => 'Pseudo :', 'id' => 'username', 'class' => 'validation')); ?>
    <?php echo $this->Form->input('email', array('label' => 'Email :', 'id' => 'email', 'class' => 'validation')); ?>
    <?php echo $this->Form->input('password', array('label' => 'Mot de passe :')); ?>
</div>
<div class="modal-footer">
    <?php echo $this->Form->submit('S\'enregistrer', array('class' => 'btn primary')); ?>
</div>
<?php echo $this->Form->end(); ?>

Elle ne contient que le formulaire d'inscription, avec certains classes qui permettent de vérifier les champs en ajax.
Bien entendu, dans mon controller, je spécifie d'appeler cette vue si la requête est réalisée en ajax.

Je n'ai aucun problème quand au fait que le div.modal soit rempli et affiché, tout fonctionne parfaitement.

XciD
Auteur

Ok merci, par contre si tu veux faire une validation de données en js sur des éléments de ton modal tu ne peux pas :/ car ils ne sont pas encore affichés :/ ou encore un datepicker

$.live ! THE fonction quand on fait du JS.

Pour poursuivre mon exemple du formulaire d'inscription en modal, je traite bien entendu les données derrières.
Il faut donc que je vérifie les champs issues de enregistrer-ajax.ctp.

Pour cela, j'utilise l'action validation() de mon controller. C'est pas encore tout à fait au point vu que validate déconne encore un peu avec la version 2 de cake ( j'en parle ici ), mais c'est déjà une excellente base de travail, plutôt que d'infliger à jquery le travail de validation. En revanche, il faut avoir défini validate dans le model.

Donc, dans un premier temps, je vérifie individuellement les champs qui ont la classe .validation, et ceci à chaque caractère tapé :

$('input.validation').live('keyup', function(){
            var champ = $(this).attr('id');
            var value = $(this).val();
            var controller = $(this).parents('form').attr('id').split('-');
            var controller = controller[0];
            validation(value, champ, controller);
    });

Donc je récupère le controller (habilement placé dans l'id du form), la valeur et le champ lui même, puis je lance ma fonction validation js.

function validation(value, champ, controller) {
    $.ajax({
        async:true,
        type:'post',
        complete:function(request, json) {
            $('#'+champ).parent('.input').children('#result-'+champ).remove();
            $('#'+champ).parent('.input').append('<div id="result-'+champ+'"></div>')
            $('#result-'+champ).empty().append(request.responseText);

            if($("#result-"+champ).find('.erreurs').size() !== 0) {
                $('#'+champ).parent('div').removeClass('success').addClass('clearfix error');
            }

            if($("#result-"+champ).find('.succes').size() !== 0) {
                $('#'+champ).parent('div').removeClass('error').addClass('clearfix succes');
            }
        },
        url:'/eclipse/'+controller+'/validation',
        data: {champ: champ, valeur: value}
    });
}

A partir de là, je spécifie une requête ajax, adressée au bon controller (d'où l'importance de passer ce paramètre, puisque le formulaire peut-être appelé depuis n'importe quelle page du site.
Du coup, je supprime le div qui accueille les messages d'erreur ou de validation, puis j'en recréé un tout neuf (non, je n'aime pas utiliser empty ^^) et j'y envoie le résultat de la requête.
Petit bonus, je teste ensuite si le message renvoyé par mon action validation a la classe erreur ou succès afin d'ajouter la classe correspondante au conteneur du message.

Et niveau php :

public function validation() {
        if($this->request->is('ajax')) {
            $ajax_data = array($this->request->data'champ'] => $this->request->data'valeur']);
            $d'user'] = $ajax_data;
            $this->User->create($ajax_data);
            $this->set($d);

            if ($this->User->validates(array('fieldList' => $this->request->data'champ']))) {
                echo('<p class="succes">Ce pseudo est disponible.</p>');
            } else {
                echo('<p class="erreurs">Ce pseudo n\'est pas disponible.</p>');
                $errors = $this->User->invalidFields();
                debug($errors);
            }
        }
    }

C'est encore en phase de débug, mais c'est fonctionnel sous certaines conditions (voir mon lien pour le problème de validate).
Déjà il faut définir la fonction comme publique, puisqu'elle peut être appelée de n'importe où sur le site.
Ensuite, on récupère les données envoyées par ajax pour initialisé un nouvel utilisateur, et pour les passer à la vue.

C'est à partir de là que l'on teste les données : si ça valide, on envoie un message correspondant (on peut penser à un case php pour renvoyer un message de succès selon le champ passé dans $ajax_data), et si les données ne sont pas valide, on se sert d'invalidFields qui renvoie quelques erreurs, mais pas toutes.

Donc actuellement, il n'est pas possible d'avoir de message relatif à chaque erreur vu le problème sur cette méthode, mais il est tout à fait possible de se contenter du true/false de validates() pour afficher un message de succès ou d'erreur.

La fin du processus n'est pas encore implantée puisque pour ma part j'ai besoin des messages d'erreurs, mais on peut faire sur la fonction d'enregistrement un test is('ajax') suivi d'un is('put'), et d'une fonction d'enregistrement.
Par contre, il serait une erreur de se passer de validates() à cette étape, puisque l'action validation n'est pas là pour filtrer, mais uniquement pour indiquer à l'utilisateur si ce qu'il a inscrit est valide ou pas.

En revanche, il faut se passer d'invalidFields dans la fonction d'enregistrement, sous peine d'obtenir les messages d'erreur ou de succès une seconde fois.
L'astuce reste de remettre à zéro le formulaire et de l'agrémenter d'un session->flash qui confirme l'enregistrement.

XciD
Auteur

Je crois bien comprendre l'ensemble de ton code, sauf ici :

$('#'+champ).parent('.input').children('#result-'+champ).remove();

result correspond à quoi ?
Par contre, pourquoi pas utiliser une validation via jQuery ( cela éviterai la fonction validation dans ton controller non ?)
Sinon tu peux utiliser dans ton controller :

$this->User->validationErrors'name'] = array("Le pseudo est deja utilisé");
$('#'+champ).parent('.input').children('#result-'+champ).remove();

Ceci correspond aux divs qui acceuillent les messages d'erreur (une div par champ) dans la vue, et que je place via jquery juste après le champ lui même. Cependant, si le champ existe déjà, je le supprime et le recréé pour y ajouter ce que je veux.
J'ai choisi ce procédé plutôt qu'un simple empty() suivi d'un append() parce que je ne sais pas forcément combien de champs j'aurais sur la page, ainsi, si je choisi de rajouter un nouveau champ, par exemple, un champ de vérification du mot de passe, jquery créera lui-même le div correspondant.
J'ai choisi de nommer ces divs #result-champ. Ainsi, celui de l'email est #result-email. C'est juste un typage logique pour m'y retrouver plus facilement.

Si je ne valide pas par jQuery, c'est parce que je souhaite utiliser les règles $validate mises en place dans mon Model. J'ai plusieurs règles par champ, beaucoup de champs (seulement 3 à la création, mais très nombreux ensuite), et c'est plus optimal et logique de les définir dans le Model et de les tester quand il le faut via validate que de redéfinir toutes mes règles dans jquery (surtout que pour le isUnique, par exemple, jquery ne le gère pas du tout en natif et il faut donc re créer une requête ajax, etc...).

De plus, ma validation marche puisque validates() me retourne bien true ou false en suivant le Model. L'erreur se situe au niveau de invalidFields qui ne me renvoie pas les bons messages, mais on peut penser que la Cake Team règlera ça rapidement.
D'autre part, ceci me permet une plus grande flexibilité dans le code.
Imaginons que je souhaite que jquery traite différemment la requête selon le résultat de validates(), je peux passer un tableau en json en spécifiant à mon controlleur en fin de parcours (donc lorsque le formulaire est envoyé), par exemple :

if ($this->User->validates()) {
                echo(json_encode(array(
    'validated' => true,
'message' => 'Vous avez bien été enregistré, vous pouvez désormais vous connecter.'
)));
            } else {
                echo(json_encode(array(
    'validated' => false,
'message' => 'Vos informations sont incorrectes. Veuillez vous référer aux messages d\'erreur du formulaire.'
)));
            }

Et ainsi, si la validation, et donc l'enregistrement a eu lieu, jquery peut tester la condition (.validated == true) et par exemple masquer le formulaire, pour afficher le message à la place, alors que dans le cas où les données ne sont pas correctes, le formulaire reste en place mais le message est tout de même affiché.

De même pour le validationErrors : j'ai défini mes différents messages d'erreur dans le $validate du Model. S'il fonctionnait convenablement, ce serait parfait : le message serait passé au div correspondant, avec la classe correspondante afin de révéler des erreurs.
Sachant que les mêmes champs sont exploités sur l'inscription et la modification du profil, autant ne pas se compliquer la tâche en les spécifiant à deux endroits différents.

XciD
Auteur

Déjà merci pour ton aide précieuse et l'ensemble des détails que tu m'as apporté.

Bon j'ai téléchargé le modal.js du bootstrap tweeter, j'ai récupéré ton code cependant j'ai un petit bug au début :/
Je m'explique, j'ai crée le lien avec la class modal-link,
J'ai récupéré ton code qui gere l'ajax et j'ai eu un premier bug : j'ai du modifier le type par get (avant 'post') car sinon cela me postait directement un nouvelle élement :/ ( Si tu peux me montrer comment tu as géré cela niveau controller cela serait intéressant :))

Maintenant mon formulaire d'ajout apparait mais pas en modal, j'ai une div modal en dessous de ma page habituelle qui est draggable, mais elle n'apparait pas comme dans le bootstrap de twitter.

D'ou cela peut-il venir ?

De rien, si on peut aider ;)

Je ne me sers pas de la classe Modal, principalement parce qu'ensuite c'est assez compliqué d'écrire avec jquery dans les différentes parties du modal à partir de la vue.
Ce que je fais, c'est que je ne créé que le backdown et le modal (tout deux sont des class , c'est important !).
Si ça n’apparaît pas en tant que modal, c'est peut être que tu as mis un id au lieu de la classe. Tu as bien importé le less du bootstrap ?

Ensuite, attention, le code qui gère l'ajax dans mon exemple gère la validation des champs et l'affichage du message !
Pour l'enregistrement, il faut créer une nouvelle fonction javascript, qui sera appelée sur le live('click') du submit, et sur le live('submit') du formulaire.

A partir de là, dans ton controller, il faut bien jongler entre le is('ajax') et le sans ajax, mais aussi avec le type de données passées.

public function enregistrer() {
        if($this->request->is('ajax')) {
            $this->layout = 'enregistrer_ajax';
            if($this->request->is('put')) {
                $d = $this->request->data;
                $d'User']'id'] = null;

                if(!empty($d'User']'password'])) {
                    $d'User']'password'] = Security::hash($d'User']'password'], null, true);
                }

                $this->User->create();
                if($this->User->save($d, true, array('username', 'password', 'email'))) {
                    $link = array('controller' => 'users', 'action' => 'activer', md5($d'User']'id'].$d'User']'password'].$d'User']'username']));

                    $this->Session->setflash('Votre compte a bien été créé.', 'notif');
                } else {
                    $this->Session->setflash('Merci de corriger vos erreurs.', 'notif');
                }
            }
        }
        else {
            if($this->request->is('post')) {
                $d = $this->request->data;
                $d'User']'id'] = null;

                if(!empty($d'User']'password'])) {
                    $d'User']'password'] = Security::hash($d'User']'password'], null, true);
                }

                $this->User->create();
                if($this->User->save($d, true, array('username', 'password', 'email'))) {
                    $link = array('controller' => 'users', 'action' => 'activer', md5($d'User']'id'].$d'User']'password'].$d'User']'username']));

                    $this->Session->setflash('Votre compte a bien été créé.', 'notif');
                } else {
                    $this->Session->setflash('Merci de corriger vos erreurs.', 'notif');
                }
            }
        }
    }

Comme tu peux le voir, en Ajax, le type de données est 'put', alors qu'il est 'post' normalement.
De plu, la requête appelle la fonction validates() lors du create, mais ça n'est pas encore optimisé, j'ai fait ça uniquement pour finaliser le processus.
A terme, l'idée sera de tester if(validates()) sur les données : si ça a lieu, alors on enregistre les données et on envoie un mail qui indique que l'utilisateur a été inscrit et qu'il a 7 jours pour confirmer son inscription par mail (la variable $link) sert a créé le lien, et un retour json qui comprendra un array avec le résultat (true) et un message de succès ; si les données ne sont pas valides, on fait un retour json avec false et un message d'erreur.

XciD
Auteur

Merci, je pense avoir résolu ton probleme pour la gestion de l'erreur. En tout cas merci pour ton aide.

public function validation() {
            $this->autoRender = false;
            if($this->request->is('ajax')) {

                $ajax_data = array($this->request->data'champ'] => $this->request->data'valeur']);
                $champ = $this->request->data'champ'];
                $d'user'] = $ajax_data;
                $this->User->create($ajax_data);

                $errors = $this->User->invalidFields();
                if (array_key_exists($champ,$errors)){
                    echo ($errors$champ][0]);
                }
            }

    }

Cela me renvoie le message d'erreur, a toi de le personnaliser :).
par contre j'ai enlever ton $this->set($d); il servait à quoi ?

Problème semi résolu, puisque quand le tableau d'erreurs est vide, il n'y a toujours pas de message. Mais c'est un truc dans ce goût là qu'il faut mettre en place temporairement.
D'un autre côté, l'appli est loin d'être terminée et publique, donc rien ne presse dans mon cas. :) En tout cas merci ;)

Pour le set, c'est une habitude de passer des données à la vue, au cas où, en phase de debug. Mais là je n'ai même pas de vue, donc en effet... ^^

De ton côté, tout fonctionne bien ? ;)

XciD
Auteur

éh bien j'ai un nouveau soucis, je suis resté sur la validation mais j'ai changé mon template de travail donc j'ai du refaire mon login.

Le login est une sorte de tab avec 3 tabs disponible : Login / Signin / Mdp oublié.
Le problème c'est que il faut générer une seul vue :/

<?php $action = $this->request->params'action'];?>
<div class="grid_12">
    <div class="box_top">
    <h2 class="icon key">Login</h2>
    <ul class="sorting">
        <li><a href="#login" <?php echo $action=='login'?'class="active"':'' ;?>>Login</a></li>
        <li><a href="#signup" <?php echo $action=='signup'?'class="active"':'' ;?>>S'enregistrer</a></li>
        <li><a href="#forgot" <?php echo $action=='password'?'class="active"':'' ;?>>Mot de pass oublié</a></li>
    </ul>   
</div>
<div class="box_content padding">
    <div class="tabs">
                <?php echo $this->Session->flash(); ?>
        <div id="login">

            <?php echo $this->Form->create(null, array('id'=>'Users-login',
                 'url' => array('controller' => 'users', 'action' => 'login'))); ?>
                <?php echo $this->Form->input('username',array(
                            'label'=>array('class'=>'left','text'=>'Login'),
                            'class'=> 'tip-stay right',
                            'title' => 'Entrer votre login',
                            'div' => 'field noline nopadding',)) ?>
                <?php echo $this->Form->input('password',array(
                            'label'=>array('class'=>'left','text'=>'Password'),
                            'class'=> 'tip-stay right',
                            'title' => 'Entrer votre mot de passe',
                            'div' => 'field',)) ?>          
                <div class="field noline nopadding">
                        <?php echo $this->Form->button("Login",array('class' => 'right','div' => 'right','type' => 'submit'));?>
                </div>
            <?php echo $this->Form->end(); ?>
        </div>
        <div id="signup">

            <?php echo $this->Form->create(null, array('id'=>'Users-signup',
                 'url' => array('controller' => 'users', 'action' => 'signup'))); ?>

                <?php echo $this->Form->input('username',array('id'=>'username',
                            'label'=>array('class'=>'left','text'=>'Login'),
                            'class'=> 'validation tip-stay right',
                            'title' => 'Choisir un login',
                            'div' => 'field noline nopadding',)) ?>

                <?php echo $this->Form->input('mail',array('id'=>'mail',
                            'label'=>array('class'=>'left','text'=>'Mail'),
                            'class'=> 'validation tip-stay right',
                            'title' => 'Entrer votre email',
                            'div' => 'field noline nopadding',)) ?>

                <?php echo $this->Form->input('password',array('id'=>'password',
                            'label'=>array('class'=>'left','text'=>'Password'),
                            'class'=> 'validation tip-stay right',
                            'title' => 'choisissez votre mot de passe',
                            'div' => 'field',)) ?>

                    <?php echo $this->Form->button("S'enregister",array('div' => 'field noline right nopadding','type' => 'submit'));?>
            <?php echo $this->Form->end(); ?>
        </div>
        <div id="forgot">

            <?php echo $this->Form->create(null, array('id'=>'Users-password',
                 'url' => array('controller' => 'users', 'action' => 'password'))); ?>
                <?php echo $this->Form->input('mail',array('id'=>'mail',
                            'label'=>array('class'=>'left','text'=>'Mail'),
                            'class'=> 'validation tip-stay right',
                            'title' => 'Entrer votre email',
                            'div' => 'field',)) ?>
                    <?php echo $this->Form->button("Mot de passe oublié",array('div' => 'field noline right nopadding','type' => 'submit'));?>
            <?php echo $this->Form->end(); ?>
        </div>
    </div>
</div>

et dans mes controller j'ai mis un $this->render('/Users/login');
J'essaye de faire les submit en post d'abord pour le cas ou il n'y a pas d'ajax et apres je pense me pencher sur l'ajax.

En normal, mon login fonctionne peut importe si je l'appel depuis /users/signup , /users/password . Il me renvoie le message d'erreur ou me redirige en cas de success.
Par contre les deux autres, j'ai un peu plus de mal :/
La fonction validation fonctionne à merveille sur le signup (avec l'ajax) donc l'utilisateur voit en temps réel si ce qu'il met est juste cependant si il laisse une erreur et fait un submit, je suis redirigé vers ma page /users/signup mais sans message d'erreur et je dois faire de nouveau submit pour avoir le message d'erreur :/

En ajax, je pourrais faire en sorte de masqué le bouton tant que ce n'est pas valide mais sans l'ajax comment faire ?

J'espere avoir été clair :)

(Par contre dans ton code, le moment ou tu recupere ar champ = $(this).attr('id'); moi je n'avais pas le champs mais un : id=UserUsername , j'ai donc du rajouter une 'id'=> 'username' pour la validation ( ce que j'aurais aimé ne pas faire ).)

En tout cas merci pour ton aide :D

S'il n'y a qu'une seule vue, il faut rediriger le requêtes ajax sur les bonnes actions du controller. Et le faire au moment de l'envoie de chacun des formulaires.
Du coup, il faut bosser sur le .live('submit') ou $('submit').live('click'), voire même les deux. Et il faut ajouter à cela un return false;, pour éviter que tu sois redirigé.

Donc on peut penser à :

$(document).ready( function () {
$('#Users-login').live('submit', function(){ login_ajax($(this).attr('action')); })
}
function login_ajax(url) {
var data = $(this).serialize();
$.ajax({
            async:true,
            type:'post',
            complete:function(request) {
                var retour = $.parseJSON(request.responseText);
                if(retour.resultat == false){
                    $(element).parent().append('<p>'+retour.message+'</p>');
                }
                else if(retour.resultat == true) {
                    $(element).parents('form').remove();
                    $('.modal').append('<div class="'+retour.container+'"><p>'+retour.message+'</p></div>');
                }
            },
            url: url,
            data: data
        });
return false;
}

Ainsi, tu es certain de rester sur la même page, que la personne clique sur le bouton submit ou qu'il fasse entrer pour soumettre le formulaire.

Pour ton second soucis, j'ai du mal à comprendre. En gros, une fois le formulaire envoyé, tu fais un redirect et un setFlash ?
Le <?php echo $this->Session->flash(); ?> doit se trouver dans le default.ctp pour que le message soit affiché, ajax ou pas. Mais dans le cas de l'Ajax, je ne vois pas bien l'intérêt d'utiliser ces deux fonctions. L'idée étant justement de ne pas changer de page.

Ce que je te conseille c'est de d'abord rendre le module entièrement fonctionnel sans ajax, puis ensuite de commencer à le coder avec, et d'aménager des conditions dans le controller, et de changer ce qui est retourné (de préférence pour du json) afin de gérer ceci comme tu veux avec jQuery.
Pour exemple, l'action login de mon controller :

public function login() {
        if($this->request->is('ajax')) {
            $this->layout = 'login_ajax';
            if($this->request->is('post') && !empty($this->request->data)) {
                $this->layout = 'login_ajax_reponse';

                if($this->Auth->login()) {
                    $pseudo = AuthComponent::user('username');
                    echo(json_encode(array(
                        'resultat' => true,
                        'message' => 'Vous êtes désormais connecté en tant que '.$pseudo.'.',
                        'container' => 'modal-body'
                    )));
                } else {
                    echo(json_encode(array(
                        'resultat' => false,
                        'message' => 'Votre pseudo ou votre mot de passe est incorrect, veuillez corriger vos informations puis réessayer.'
                    )));
                }
            }
        } else {
            if($this->request->is('post')) {
                if($this->Auth->login()) {
                    $this->Session->setFlash('Vous êtes maintenant connecté.', 'notif');
                    $this->redirect('/');
                } else {
                    $this->Session->setFlash('Identifiants incorrects', 'notif');
                }
            }
        }
    }

On y voit bien que les comportements sont complètement différents si on utilise ajax ou si on le fait normalement.
Mais je n'ai peut-être pas bien compris ton problème.

Pour le dernier point, on peut en effet s'en passer, en ajoutant un système de récupération du champ dans le is('ajax') du controller.
On peut imaginer récupérer l'id du champ (par défaut, Cake le nomme en effet ModelChamp). Sauf que c'est le bazar : j'avais tenté un truc en récupérant le controller dans les request->params, puis en faisant un explode sur l'id du champ, en ayant au préalable tout passé en minuscule pour qu'il n'y ai pas d'erreur due à la casse, mais le souci vient du fait que Cake renvoie le nom du controller au pluriel... Donc j'ai préféré le passer directement dans l'id. C'est beaucoup plus simple que la dizaine de ligne que je commençais à avoir dans mon controller juste pour ça.

XciD
Auteur

Je reformule :D

J'aimerais que mon formulaire fonctionne d'abord sans allez dans l'ajax ( ce que tu as dit :)) car apres pour l'ajax avec l'ensemble des exemples que tu m'as donnée je pense réussir.

Avant d'allez dans l'ajax, j'essaye de débuger mon formulaire.
En gros, il y a 3 formulaires dans une pages et un seul visible à la fois (système de tab via js).
Les trois forumaires appel une action via :

<?php echo $this->Form->create('User', array('id'=>'Users-login',
                 'url' => array('controller' => 'users', 'action' => 'login'))); ?>

Au dessus de chaque formulaire j'ai un

<?php echo $this->Session->flash('login'); ?>

('login' ou 'signup' ou 'password')
Ainsi dans mon controller j'envoie mon message (par exemple):

$this->Session->setFlash('Corrigé vos erreurs',"aides/notif",array('taille'=>'small','type'=>'error'),'signup');

Si je met le $this->Session->flash dans le layout par default il n'apparaitra pas ou je le désire.

Au niveau des notifications il n'y a pas de probleme.

Quand je suis à la racine du site (controller => user / action => login ):
Formulaire : login :
Validation OK
Message d'erreur OK
Formulaire : signup / password :
Validation OK
Par contre si je me trompe dans un champ et que je valide je suis redirigé vers /users/signup ou /users/password mais sans message d'erreur et je dois donc re cliquer sur submit pour voir apparaitre le message.

Maintenant que je suis soit dans signup ou password :
Le formulaire concerné fonctionne bien (validation OK et erreur OK)
Le formulaire login ce comporte bien :
validation OK
erreur OK (redirection vers / avec message d'erreur) => comportement souhaité sur l'ensemble de mes formulaires
Le dernier (signup ou password selon ou je suis) validation ok et erreur no ok.

Voici mon UsersController allégé

<?php
class UsersController extends AppController 
{
    public function signup(){
        $this->layout = 'login';
        $this->render('/Users/login');
        if($this->request->is('post') || $this->request->is('put')){
            $d = $this->request->data;
            $d'User']'id'] = null;
            if (!empty($d'User']'password'])) {
                $d'User']'password'] = Security::hash($d'User']'password'],null,true);
            }
            if($this->User->save($d,true,array('username','password','mail'))){
                $link = array('controller'=>'users','action'=>'activate',$this->User->id.'-'.md5($d'User']'password']));
                App::uses('CakeEmail','Network/Email');
                $mail = new CakeEmail();
                // blablabla pour le mail
                $this->Session->setFlash("Votre Compte a bien été crée, vous allez recevoir un mail pour valider votre compte ","aides/notif",array('taille'=>'small','type'=>'success'),'login');
                $this->redirect('/');
            }
            else
            {
                $this->Session->setFlash('Corrigé vos erreurs',"aides/notif",array('taille'=>'small','type'=>'error'),'signup');
            }
        }
    }

    public function login(){

        $this->layout = 'login';
        $this->set('title_for_layout', "Connexion");
        if (AuthComponent::user('id')){
            $this->redirect('/membre/pages');
            }
        else{   
            if($this->request->is('post')){
                if ($this->Auth->login()) {
                    $this->User->id = $this->Auth->user('id');
                    $this->User->saveField('lastlogin',date('Y-m-d H:i:s'));
                    $this->Session->setFlash("Vous êtes maintenant connecté","aides/notif2",array('type'=>'success'));
                    $this->redirect('/membre/pages/index');
                }else{
                    $this->Session->setFlash('Identifiants incorrects',"aides/notif",array('type'=>'error','taille'=>'small'),'login');
                }   
            }
        }       

    public function password(){
        $this->layout = 'login';
        $this->render('/Users/login');
        if (!empty($this->request->params'named']'token'])) {
            $token = $this->request->params'named']'token'];
            $token = explode('-',$token);
            $user = $this->User->find('first',array(
                'conditions' => array(
                    'id' => $token[0], 
                    'MD5(User.password)' => $token[1], 
                    'active' => 1)
                    )
                );
            if ($user) {
                $this->User->id = $user'User']'id'];
                $password = substr(md5(uniqid(rand(),true)),0,8);   
                $this->User->saveField('password',Security::hash($password,null,true));
                $this->Session->setFlash("Votre mot de passe a été réinitialiser : Voici votre nouveau mot de pass : $password ",'aides/notif',array('taille'=>'small','type'=>'warning'),'login');
                $this->redirect(array('controller'=>'users','action'=>'login'));
            }else {
                $this->Session->setFlash("Le lien n'est pas valide",'aides/notif',array('taille'=>'small','type'=>'error'),'password');
            }
        }
        if ($this->request->is('post') || $this->request->is('put')) {
            $v = current($this->request->data);
            $user = $this->User->find('first',array(
                'conditions' => array(
                    'mail'=>$v'mail'],
                    'active' => 1)));

            if (empty($user)) {
                $this->Session->setFlash('Aucun utilisateur ne correspond a cet email','aides/notif',array('taille'=>'small','type'=>'error'),'password');
            }else {
                App::uses('CakeEmail','Network/Email');
                $link = array('controller'=>'users','action'=>'password','token' => $user'User']'id'].'-'.md5($user'User']'password'])); 
                $mail = new CakeEmail();
                $mail->from('adrien@gmail.com')
                    ->to($user'User']'mail'])
                    ->subject('Test :: Mot de passe oublié')
                    ->emailFormat('html')
                    ->template('mdp')
                    ->viewVars(array('username'=>$user'User']'username'],'link'=>$link))
                    ->send();
                $this->Session->setFlash('Un email viens de vous être envoyé','aides/notif',array('taille'=>'small','type'=>'success'),'login');
                debug($link);   
                $this->redirect('/');
            }   
        }
    }

    public function validation() {
        $this->autoRender = false;
        if($this->request->is('ajax')) {
            $ajax_data = array($this->request->data'champ'] => $this->request->data'valeur']);
            $champ = $this->request->data'champ'];
            $d'user'] = $ajax_data;
            $this->User->create($ajax_data);
            $errors = $this->User->invalidFields();
                if (array_key_exists($champ,$errors)){
                    echo ($errors$champ][0]);
                }else{
                        echo ("1");
                    }
        }

    }
}
?>

et ma vue login.ctp

<?php $action = $this->request->params'action'];?>
<div class="grid_12">
    <div class="box_top">
    <h2 class="icon key">Login</h2>
    <ul class="sorting">
        <li><a href="#login" <?php echo $action=='login'?'class="active"':'' ;?>>Login</a></li>
        <li><a href="#signup" <?php echo $action=='signup'?'class="active"':'' ;?>>S'enregistrer</a></li>
        <li><a href="#forgot" <?php echo $action=='password'?'class="active"':'' ;?>>Mot de pass oublié</a></li>
    </ul>   
</div>
<div class="box_content padding">
    <div class="tabs">

        <div id="login">
            <?php echo $this->Session->flash('login'); ?>
            <?php echo $this->Form->create('User', array('id'=>'Users-login',
                 'url' => array('controller' => 'users', 'action' => 'login'))); ?>
                <?php echo $this->Form->input('username',array(
                            'label'=>array('class'=>'left','text'=>'Login'),
                            'class'=> 'tip-stay right',
                            'title' => 'Entrer votre login',
                            'div' => 'field noline nopadding',)) ?>
                <?php echo $this->Form->input('password',array(
                            'label'=>array('class'=>'left','text'=>'Password'),
                            'class'=> 'tip-stay right',
                            'title' => 'Entrer votre mot de passe',
                            'div' => 'field',)) ?>          
                <div class="field noline nopadding">
                        <?php echo $this->Form->button("Login",array('class' => 'right','div' => 'right','type' => 'submit'));?>
                </div>
            <?php echo $this->Form->end(); ?>
        </div>
        <div id="signup">
            <?php echo $this->Session->flash('signup'); ?>
            <?php echo $this->Form->create('User', array('id'=>'Users-signup',
                 'url' => array('controller' => 'users', 'action' => 'signup'))); ?>

                <?php echo $this->Form->input('username',array('id'=>'username',
                            'label'=>array('class'=>'left','text'=>'Login'),
                            'class'=> 'validation tip-stay right',
                            'title' => 'Choisir un login',
                            'div' => 'field noline nopadding',)) ?>

                <?php echo $this->Form->input('mail',array('id'=>'mail',
                            'label'=>array('class'=>'left','text'=>'Mail'),
                            'class'=> 'validation tip-stay right',
                            'title' => 'Entrer votre email',
                            'div' => 'field noline nopadding',)) ?>

                <?php echo $this->Form->input('password',array('id'=>'password',
                            'label'=>array('class'=>'left','text'=>'Password'),
                            'class'=> 'validation tip-stay right',
                            'title' => 'choisissez votre mot de passe',
                            'div' => 'field',)) ?>
                <?php echo $this->Form->button("S'enregister",array('div' => 'field noline right nopadding','type' => 'submit'));?>
            <?php echo $this->Form->end(); ?>
        </div>
        <div id="forgot">
        <?php echo $this->Session->flash('password'); ?>
            <?php echo $this->Form->create('User', array('id'=>'Users-password',
                 'url' => array('controller' => 'users', 'action' => 'password'))); ?>
                <?php echo $this->Form->input('mail',array('id'=>'mail',
                            'label'=>array('class'=>'left','text'=>'Mail'),
                            'class'=> 'validation tip-stay right',
                            'title' => 'Entrer votre email',
                            'div' => 'field',)) ?>
                    <?php echo $this->Form->button("Mot de passe oublié",array('div' => 'field noline right nopadding','type' => 'submit'));?>
            <?php echo $this->Form->end(); ?>
        </div>
    </div>
</div>

En tout cas merci de prendre le temps de me lire, sinon pour le fait que le tableau est vide et qu'il n'y a pas de message, tu fais une boucle sur le champ et en fonction tu genere un message personnalisé. non ?
Moi je n'ai pas besoin du message, je renvoi un 1 si c'est ok et j'ai une alternance entre une croix rouge et un petit v vert en fonction du 1.

XciD
Auteur

Solution qui fonctionne mais qui est un peu bete :
enlever le $this->render(users/login) et copier 3 fois la vue...
Ca fonctionne mais c'est pas ce qui il y a de plus optimiser

Si vous avez une solution je suis preneur :)

J'ai beau cherché, je ne vois pas d'où cela peut venir.

Il pourrait être bon de faire un debug de $this->request et de $this->Session pour voir si les flash sont bien passés à ta vue quand tu n'est pas sur l'action relative à cela au départ.

Les données des champs sont bien ré affichées dans le formulaire correspondant en cas d'erreur ?

Je repasse en fin d'aprèm pour te filer un coup de main ;)

XciD
Auteur

$this->Session renvoie un nombre infini d'information et rien trouvé sur flash.
Oui les données apparaissent directement
Je pense avoir compris le process => je fais un submit, le flash est envoyé à ma vue sur laquelle je suis mais apres j'ai une redirection donc je perd le flash, car quand je fais précedent je vois le flash...

Mais c'est bizarre que cela fonctionne avec sans le $this->render.

Bon c'est pas optimiser mais cela fonctionne, je vais m'occuper de l'ajax :)
Merci de ton aide

Ah! J'ai toujours pas compris le $this->render pour être honnête... :/ Mais si ça marche c'est l'essentiel.
Si tu as besoin pour l'Ajax, n'hésite pas.

Et si tu as besoin de mon code, c'est avec plaisir ;)

XciD
Auteur

$this->render(Users/login) en gros tu oblige le controller a passer par la vue qui se trouve View/Users/login au lieu de la vue predefine.
Je pense que tu vas m'aider si je n'y arrive pas :D.
Je débute avec js / php surtout que js ca me parrait imbuvable :/

XciD
Auteur

Tu as quoi dans login_ajax.ctp et login_ajax_response ?
Pourquoi ne pas tout simplement supprimer la vue en fesant $this->autoRender = false; ??

Bon ya un truc que je dois pas capter dans ce code :

function login_ajax(url) {
    var data = $(this).serialize();
    $.ajax({
                async:true,
                type:'post',
                complete:function(request) {
                    console.log(request.responseText);

                },
                url: url,
                data: data
            });
    return false;
    }
(function ($){
    $(document).ready(function() {
    $('#Users-signup').live('submit', function(){ login_ajax($(this).attr('action'));})

Déja, il me bloque pas le lien HTML alors que si je met un return false comme ci dessous cela fonctionne:

$('#Users-signup').live('submit', function(){ login_ajax($(this).attr('action'));return false;})

Et dans la fonction c'est pareil, je suis resté une heure a cherché pourquoi serialize ne fonctionnait pas mais avec ceci ca marche :

var data = $('#Users-signup').serialize();

La je comprend pas :/

XciD
Auteur

J'ai envie de te dire que j'ai réussi :D.
Par contre le login je l'ai pas fait en ajax, je me suis dit qu'un login normal suffit, en effet en cas de réussite j'ai pas réussir à faire une redirection (vue que les layout sont entièrement différent) et affiché un message sur la nouvelle page.

J'aimerais revenir sur ton problème avec les messages d'erreurs, en gros qu'es qu'il te faut ? parce que moi j'ai réussi

Tiens, ma réponse n'est pas passée...
Je disais que j'avais fini par comprendre $this->render() ;)

Ensuite, si ton this ne marche pas, c'est que tu l'appelle depuis la fonction, et pas depuis l'évènement. Du coup, il ne se réfère plus à rien. Alors qu'à l'évènement, il se réfère à l'élément du DOM qui a subit l'action.
Le seul moyen de le passer au fonction, c'est de passer un paramètre "element" :

var element = $(this);

Mais dans bien des cas c'est inutile.

De même, ton return false est dans la fonction, et pas dans l'évènement, alors que c'est l'évènement en question qu'on essaie d'empêcher. Il faut donc le mettre là :

$('#Users-signup').live('submit', function(){
    login_ajax($(this).attr('action'));
    return false;
});

Ensuite, si serialize ne fonctionnait pas, c'est parce que tu l'appelais avec this depuis une fonction. ^^

Voila voila.

Concernant mes messages d'erreurs, je t'invite à te rendre sur le sujet approprié ;) En gros, les messages d'erreurs renvoyés sont :

  • en double
  • pas uniquement sur le champ voulu
  • mal indexés
  • inexistant dans certains cas
XciD
Auteur

Sisi j'ai vu :).
Ca à marché :)

relis mon post ;)

J'ai vu ton autre post après, je sais pas ce qu'il se passe, je vois pas le message que j'ai tapé hier soir...