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

jQuery Discussion :

Recharger les données / plugin [Plugin]


Sujet :

jQuery

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    710
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 710
    Par défaut Recharger les données / plugin
    Bonjour,

    Je suis en train d'organiser mon code spaghettis jQuery en plugin.
    Le premier plugin que je crée permet de générer un tableau avec des données issues d'une requête Ajax, des en tête de colonnes qui permettent de trier l'affichage des données et une pagination permettant de naviguer dans les lignes du tableau, affiché par 50 résultats.

    J'ai réussi à créer mon plugin et mon tableau s'affiche. Mon souci provient des actions qui permettent de recharger les données du tableau (tri sur colonne, navigation dans la pagination du tableau).

    Avant le plugin, j'avais une fonction (afficherTableau()) qui me permettait :
    1. de récupérer les paramètres de mon tableau (colonne de tri, numéro de page à afficher)
    2. d'afficher le tableau en fonction des paramètres

    Dans l'affichage du tableau, j'ajoutais les événements permettant de le recharger en appelant la fonction.

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    function afficher_tableau(){
        // instruction de récupération des paramètres
        // recherche Ajax des données
        // affichage du tableau
        // evenement de rechargement
        $('.entete').click(function(){
            afficher_tableau();
        })
        $('.pagetableau').click(function(){
            afficher_tableau();
        })
    }


    Du coup, comme ma fonction a été remplacée par un plugin, je ne sais pas comment recharger les données. J'aurai tendance à créer une fonction dans mon plugin et que cette fonction se charge d'afficher les données. Toujours à l'intérieur de mon plugin j'ajouterai les instructions concernant les événements qui rechargent les données du tableau.

    Est-ce la bonne façon de faire ? je me dit que non parce que sinon la réorganisation en plugin n'est - je trouve - pas super utile puisqu'on retrouve la fonction dans celui-ci avec les événements... Mais du coup comment faire ? Auriez-vous une piste ?

    Merci de vos conseils !

  2. #2
    Expert confirmé
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 099
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 099
    Par défaut
    Salut,
    Et si tu nous montrais ton début de code de plugin ?

    En théorie, un plugin jQuery s’utilise comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $('.montableau').monPlugin({ option1: "valeur1", option2: "valeur2", ... });
    Parmi les options passées, tu peux mettre l’adresse à laquelle récupérer les données. Tu peux utiliser par exemple .data() pour stocker des données à même l’élément <table> si tu as besoin.

    Je t’invite à lire, si tu ne les as pas déjà lues, les deux pages How to Create a Basic Plugin et Advanced Plugin Concepts. Si tu n’es pas à l’aise avec l’anglais, je peux te faire un résumé
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  3. #3
    Membre éclairé Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    710
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 710
    Par défaut
    Merci de ta réponse.

    Voici le code (simplifié) de mon plugin.

    Code javascript : 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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
     
     
    (function ( $ ) {
        $.fn.monTab = function(parametres){
            return this.each(function(){
     
                var defaults = {
                    // (...) paramètres par défaut
                }
     
                // mix des paramètres par défaut + celui de l'utilisateur
                var opts = $.extend( {}, defaults, parametres);
     
                // appel de la fonction qui affiche le tableau
                searchData();
     
                function searchData(){
     
                    // on vide l'en tête du tableau
                    $(opts.cible + ' thead').html('');
     
                    // construction de la ligne d'entête
                    ligne = '';				
                    $.each(opts.colonnesDuTableau, function(k, c){
                        ligne += '<th class="headcol" data-el-col="'+k+'">'+c.libelleColonne+'</th>';
                    })
     
                    // affichage de la ligne d'en tête
                    $(opts.cible + ' thead').html('<tr>' + ligne + '</tr>');
     
                    // (...) / instructions diverses sur la colonne à trier et le sens du tri
     
                    $.ajax({
                        // appel de la méthode de récupération des données
                    })
                    .done(function(retour) {
     
                        // on vide le corps du tableau
                        $(opts.cible + ' tbody').html('');
     
                        // parcours des résultats
                        $.each(retour.resultats.liste, function(i, item) {
     
                            // construction de la ligne
                            ligne = '';
                            $.each(opts.colonnesDuTableau, function(k, c){
                                ligne += '<td>'+item[c.colonne]+'</td>';
                            })
     
                            // ajout de la ligne au body du tableau
                            $(opts.cible + ' tbody').append('<tr>' + ligne + '</tr>');
                        })
     
                        // nettoyage du div pagination
                        $('.pagination').html('');
     
                        // parcours des éléments de pagination construits côté serveur et renvoyés dans le resultat de l'appel Ajax
                        $.each(retour.resultats.pages, function(i, item) {
                            // ajout des pages clicables dans le div de pagination
                            $('.pagination').append('<li class="page-item '+(item.class != undefined ? item.class : '')+'" data-el-n="'+i+'"><a class="page-link" href="#">'+item.val+'</a></li>');
                        })
     
                        // clic sur la pagination
                        $('.page-item').click(function(){
                            // instruction de modification de la page active
                            // (...)
                            // appel de la fonction pour recharger les données
                            searchData();
                        })
     
                        // tri sur une entête de colonne
                        $('.headcol').click(function(){
                            // instruction de modification de l'en tête de référence pour le tri + le sens de tri
                            // appel de la fonction pour recharger les données						
                            searchData();
                        })
                    })
                }
            })
        };
    }( jQuery ));


    Et le code de l'appel :
    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
        $('#tabData').monTab({
          'url' : getList,
          'colonnes' : {
              0 : {'libelle' : 'Nom',     colonne : 'nom' },
              1 : {'libelle' : 'Prénom', colonne : 'prenom' }
            },
          'cible' : '#tabData'
        });

    J'ai suivi les liens que tu m'as fournis (et d'autres qui y ressemblent très fortement : open class rooms, developpez...). Mais je ne crois pas y avoir trouvé la réponse à ma question.

    Avant de tenter de faire un plugin, j'avais une fonction (searchData) à laquelle je passais des paramètres (url, tri, filtre, colonne de tr, page à afficher...).
    Et avec ce plugin, je n'ai pas trouver la façon de m'affranchir de cette fonction que j'appelle lorsque le clic sur les en têtes de colonne (headcol) ou que j'utilise la pagination (page-item)
    Le plugin me permet éventuellement de gérer plus proprement les variables, mais je me dis que je dois passer à côté d'une implémentation plus clean...

    Merci !

  4. #4
    Expert confirmé
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 099
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 099
    Par défaut
    Tu as quelques variables non déclarées (à chaque fois c’est ligne). C’est mauvais car, implicitement, ça fait une variable globale. Utilise le mode strict pour détecter ça.

    Tu as l’intuition que ton code est moins propre maintenant que searchData n’a plus de paramètres. C’est une bonne intuition Dans l’état actuel, searchData() puise ses données dans la variable opts qui a été déclarée à l’extérieur, autrement dit elle va chercher les infos dans la portée de niveau supérieur. Si tu n’es pas à l’aise avec le concept de portée, je te conseille de lire la première partie de Trois fondamentaux de JavaScript.

    Note que l’article date de 2011 et qu’à l’époque, let n’existait pas. Tu peux choisir de remplacer tous les var par des let, généralement ça ne pose pas de problème, et quand ça en pose ils sont faciles à corriger. (Tu peux aussi choisir d’utiliser les deux, mais ça rend les choses confuses, je ne te le conseille pas.)

    Passer des paramètres permet de réduire le couplage : ça permet à la fonction d’être « aveugle » aux données qui l’entourent, et d’utiliser uniquement les informations fournies par le code appelant.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function searchData(options) {}
    Et là, même pas besoin d’appeler $.extend() car toi seul maîtrises l’utilisation de cette fonction, elle n’est pas accessible de l’extérieur.

    Actuellement, ta fonction searchData() est locale à la fonction $.fn.monTab = function (parametres) { ... }. C’est comme si elle était déclarée avec var. Par conséquent, à chaque appel à $().monTab(), une nouvelle fonction searchData() est créée et gardée en mémoire grâce au mécanisme des fermetures (closures).
    Donc si on déplace cette fonction, en plus de réduire le couplage, on améliore aussi un peu les performances. Je propose de la placer dans la function ( $ ) {}, à côté de la fonction $.fn.monTab. Et on va utiliser const pour indiquer en plus au moteur (et aux humains qui lisent le code) qu’on ne va jamais modifier cette variable. Et tant qu’on y est, déclarons aussi les options par défaut au même niveau :

    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
    (function ( $ ) {
      'use strict';
     
      const defaults = {};
     
      const searchData = function (options) {};
     
      $.fn.monTab = function (parametres) {};
     
    }( jQuery );
    Il faudrait également veiller à ce que les gestionnaires de .click() ne soient enregistrés qu’une seule fois. Actuellement dans ton code, chaque appel à searchData() inscrit deux gestionnaires de clic, qui sont ensuite détachés quand les éléments sont détruits lors de l’appel suivant à searchData(), qui inscrit à nouveau deux gestionnaires, etc.

    Pour être un peu plus efficaces, on peut inscrire une seule fois les gestionnaires de clic. Et pour s’assurer qu’ils ne sont inscrits qu’une seule fois, on va mettre un « marqueur » sur le tableau cible pour voir si le plugin a déjà été utilisé dessus. C’est là que .data() va nous être utile.

    Il y a un autre petit problème : $('.page-item') sélectionne tous les éléments correspondants dans la page. S’il y a plusieurs tables à paginer dans la même page, ça va créer des conflits. Pour éviter ça, on va utiliser .on() sur la table cible elle-même. Cette technique est parfois appelée délégation d’évènements.

    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
    20
    21
    $.fn.monTab = function (parametres) {
      var opts = $.extend( {}, defaults, parametres );
     
      return this.each(function () {
        var tableCible = $(this);
     
        …
     
        if (!tableCible.data('deja-appele')) {
          tableCible.on('click', '.page-item', function () {});
     
          tableCible.on('click', '.headcol', function () {});
     
          tableCible.data('deja-appele', true);
        }
      }); // this.each
    };
    Enfin, tu l’auras sûrement compris, pour utiliser la nouvelle version de searchData(), il faut lui passer un objet d’options en paramètre. Cet objet est tout simplement opts. Mais cette fois, ce n’est plus une variable de la portée supérieure, c’est un paramètre de sa propre portée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    const searchData = function (options) {
      … options.cible …
      … options.colonneDuTableau …
      …
    };
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  5. #5
    Membre éclairé Avatar de grinder59
    Inscrit en
    Septembre 2005
    Messages
    710
    Détails du profil
    Informations forums :
    Inscription : Septembre 2005
    Messages : 710
    Par défaut
    Citation Envoyé par Watilin Voir le message
    Tu as quelques variables non déclarées (à chaque fois c’est ligne). C’est mauvais car, implicitement, ça fait une variable globale. Utilise le mode strict pour détecter ça.
    Oui, c'est un oubli ! :-) merci !

    Citation Envoyé par Watilin Voir le message
    Tu as l’intuition que ton code est moins propre maintenant que searchData n’a plus de paramètres. C’est une bonne intuition Dans l’état actuel, searchData() puise ses données dans la variable opts qui a été déclarée à l’extérieur, autrement dit elle va chercher les infos dans la portée de niveau supérieur. Si tu n’es pas à l’aise avec le concept de portée, je te conseille de lire la première partie de Trois fondamentaux de JavaScript.

    Note que l’article date de 2011 et qu’à l’époque, let n’existait pas. Tu peux choisir de remplacer tous les var par des let, généralement ça ne pose pas de problème, et quand ça en pose ils sont faciles à corriger. (Tu peux aussi choisir d’utiliser les deux, mais ça rend les choses confuses, je ne te le conseille pas.)
    Je connais le concept de portée des variables, mais je reconnais avoir quelques lacunes théoriques qui se ressentent sur mon implémentation. Je vais vite me mettre à jour en consultant l'article sur les fondamentaux...

    Citation Envoyé par Watilin Voir le message
    Passer des paramètres permet de réduire le couplage : ça permet à la fonction d’être « aveugle » aux données qui l’entourent, et d’utiliser uniquement les informations fournies par le code appelant.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function searchData(options) {}
    Et là, même pas besoin d’appeler $.extend() car toi seul maîtrises l’utilisation de cette fonction, elle n’est pas accessible de l’extérieur.
    Compris et je partage cet avis. Au niveau supérieur de la fonction je récupère et mixe les paramètres par défaut et ceux transmis par l'utilisateur et ensuite, comme tu le dis par la suite, je transmets l'ensemble en paramètre à la fonction afin que celle-ci ne bosse qu'avec des variables de sa portée dont on maîtrise la valeur et que la-dite fonction n'est pas obligée d'aller chercher plus haut (et éventuellement une variable avec le même nom ayant une valeur qu'on ne maîtrise pas)

    Citation Envoyé par Watilin Voir le message
    Actuellement, ta fonction searchData() est locale à la fonction $.fn.monTab = function (parametres) { ... }. C’est comme si elle était déclarée avec var. Par conséquent, à chaque appel à $().monTab(), une nouvelle fonction searchData() est créée et gardée en mémoire grâce au mécanisme des fermetures (closures).
    Donc si on déplace cette fonction, en plus de réduire le couplage, on améliore aussi un peu les performances. Je propose de la placer dans la function ( $ ) {}, à côté de la fonction $.fn.monTab. Et on va utiliser const pour indiquer en plus au moteur (et aux humains qui lisent le code) qu’on ne va jamais modifier cette variable. Et tant qu’on y est, déclarons aussi les options par défaut au même niveau :

    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
    (function ( $ ) {
      'use strict';
     
      const defaults = {};
     
      const searchData = function (options) {};
     
      $.fn.monTab = function (parametres) {};
     
    }( jQuery );
    Nickel ! merci !


    Citation Envoyé par Watilin Voir le message
    Il faudrait également veiller à ce que les gestionnaires de .click() ne soient enregistrés qu’une seule fois. Actuellement dans ton code, chaque appel à searchData() inscrit deux gestionnaires de clic, qui sont ensuite détachés quand les éléments sont détruits lors de l’appel suivant à searchData(), qui inscrit à nouveau deux gestionnaires, etc.

    Pour être un peu plus efficaces, on peut inscrire une seule fois les gestionnaires de clic. Et pour s’assurer qu’ils ne sont inscrits qu’une seule fois, on va mettre un « marqueur » sur le tableau cible pour voir si le plugin a déjà été utilisé dessus. C’est là que .data() va nous être utile.
    Ah ben oui sapristi, je n'y avais pas pensé ! Perso, je trouve que ça fait un peu "système D" de devoir gérer une variable pour vérifier si on est déjà passé dans la boucle, n'y a-t-il pas moyen de tester l'existence que sur cette classe, le click est déjà lié à une fonction ?

    Citation Envoyé par Watilin Voir le message
    Il y a un autre petit problème : $('.page-item') sélectionne tous les éléments correspondants dans la page. S’il y a plusieurs tables à paginer dans la même page, ça va créer des conflits. Pour éviter ça, on va utiliser .on() sur la table cible elle-même. Cette technique est parfois appelée délégation d’évènements.
    Simple oubli dont je me serai rendu compte en incluant plusieurs tableaux dans la même page (ce qui ne semble pas être l'objectif pour le moment). Mais je vais rectifier, ça m'évitera des galères ultérieures (ou moi ou mon successeur)
    Enfin, tu l’auras sûrement compris, pour utiliser la nouvelle version de searchData(), il faut lui passer un objet d’options en paramètre. Cet objet est tout simplement opts. Mais cette fois, ce n’est plus une variable de la portée supérieure, c’est un paramètre de sa propre portée.
    [/QUOTE]

    Citation Envoyé par Watilin Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    const searchData = function (options) {
      … options.cible …
      … options.colonneDuTableau …
      …
    };
    Oui, compris ! Après, je n'étais pas perturbé par le fait de récupérer des variables de niveau supérieur car mon plugin n'avais qu'une seule utilité et même si la fonction allait chercher les variables plus haut, celles-ci restaient, du moins je le pensais, encapsulées dans le plugin. Une implémentation plus organisée avec une meilleure maîtrise de la portée des variables est préférable. Le code sera plus maîtrisé, lisible et maintenable...

    Après, dans ton organisation, tu sors la fonction searchData. Personnellement, je souhaitais la mettre à l'intérieur du plugin afin qu'elle soit locale à celui-ci et qu'elle ne puisse pas être utilisée par ailleurs, ce qui avec un code conséquent, pourrait porter à confusion. 1 plugin = 1 fichier avec tout ce qu'il faut dedans. Pas besoin de se traîner des fichiers super longs ou de multiples fichiers de fonctions partagés entre plusieurs plugins... Et dans la mesure où il n'y aurait qu'un tableau par page, on n'avait pas besoin d'optimiser car la fonction n'était déclarée qu'une seule fois.

    en tous cas, merci pour le temps que tu as pris à me répondre !

  6. #6
    Expert confirmé
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 099
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 099
    Par défaut
    Citation Envoyé par grinder59 Voir le message
    Perso, je trouve que ça fait un peu "système D" de devoir gérer une variable pour vérifier si on est déjà passé dans la boucle, n'y a-t-il pas moyen de tester l'existence que sur cette classe, le click est déjà lié à une fonction ?
    Non, il n’y a pas moyen de connaître les écouteurs d’évènements attachés à un élément. Avant Firefox Quantum, l’extension Firebug permettait de faire ça (je ne sais pas si c’est toujours le cas), mais du code JS non privilégié n’a pas cette capacité. La solution « système D » est une des moins pire selon moi, quand on privilégie la clarté du code et la simplicité de mise en œuvre. Et je trouve qu’elle exprime bien l’intention de « faire quelque chose seulement la première fois ».

    Il y a une solution équivalente, je ne sais pas si tu la trouveras meilleure mais je te la propose. Il s’agit de garder en mémoire, dans la « capsule » (dans la même portée que searchData() et l’objet defaults, les éléments DOM sur lesquels le plugin a déjà été appelé.
    Dans mon exemple je vais utiliser des références sur les objets DOM « nus » (sans objet jQuery autour), pour pouvoir comparer les références directement avec .includes() (.indexOf() marche aussi, ça fait juste un code légèrement moins lisible).
    J’ai besoin des objets DOM nus car les objets renvoyés par jQuery ne sont pas garantis identiques, mêmes s’ils contiennent la même chose.

    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    (function ( $ ) {
      'use strict';
     
      const elementsInitialises = [];
     
      …
     
      $.fn.monTab = function (parametres) {
        …
     
        return this.each(function () {
          var tableCible = $(this);
          var tableCibleNue = this; // ou tableCible.get(0)
     
          …
     
          if (!elementsInitialises.includes(tableCibleNue)) {
            tableCible.on('click', '.page-item', function () {});
     
            tableCible.on('click', '.headcol', function () {});
     
            elementsInitialises.push(tableCibleNue);
          }
        }); // this.each
      };
    Après, dans ton organisation, tu sors la fonction searchData. Personnellement, je souhaitais la mettre à l'intérieur du plugin afin qu'elle soit locale à celui-ci et qu'elle ne puisse pas être utilisée par ailleurs
    D’une certaine manière, elle est toujours à l’intérieur du plugin. Un plugin, ce n’est pas seulement la fonction ajoutée à $.fn, c’est aussi les variables qui vont avec (searchData(), defaults, etc.). Ces variables sont protégées dans la capsule, la (function ( $ ) {}(jQuery)) qui te sert à déclarer le plugin. Cette fonction est appelée une seule fois, et crée une fermeture qui garde en vie les variables nécessaires au bon fonctionnement de $.fn.monTab. Fais des tests, tu verras que ces données ne sont pas accessibles de l’extérieur. C’est la mécanique interne du plugin, toi seul en as le contrôle.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

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

Discussions similaires

  1. [MySQL] Comment recharger les données d'un formulaire ?
    Par Benoit017 dans le forum PHP & Base de données
    Réponses: 3
    Dernier message: 07/05/2013, 01h47
  2. Hibernate ne recharge pas les données
    Par CrazySeb dans le forum Hibernate
    Réponses: 2
    Dernier message: 21/07/2008, 13h57
  3. [AJAX] Recharger les données d'un tableau
    Par Herman dans le forum Général JavaScript
    Réponses: 36
    Dernier message: 01/07/2008, 14h05
  4. Recharger les données d'un fichier
    Par solean45 dans le forum Débuter
    Réponses: 4
    Dernier message: 20/12/2007, 15h23

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