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

AJAX Discussion :

Présentation de mon approche de modification de document en AJAX (donnez votre avis)


Sujet :

AJAX

  1. #1
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    740
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 740
    Points : 805
    Points
    805
    Par défaut Présentation de mon approche de modification de document en AJAX (donnez votre avis)
    Bonjour à tous,

    J'ai créé un nouvel outil pour lequel j'aimerais quelques avis, svp.

    Il s'agit d'une bibliothèque vraiment minuscule (moins de 2Ko), servant à modifier des pages HTML, en AJAX, basée sur window.fetch et orientée inversion de contrôle.

    Ce que j'entends par inversion de contrôle, c'est que plutôt que de dire à l'interpréteur d'appeler une fonction en retour d'un appel AJAX, afin de lui dire que faire ensuite, on va simplement écouter la réponse du serveur et agir en fonction du contenu reçu, via des listeners.

    En gros, lorsque l'utilisateur charge une page, on définit toute une série de listeners et il n'y a plus rien à faire ensuite, on n'envoie donc pas de JS avec nos réponses AJAX.

    Un des avantages, c'est qu'on ne doit pas tester ce qu'on a reçu, pour savoir comment le traiter (exemple, l'utilisateur a fait une action mais le serveur répond par une box de login car sa session a expiré, a mené vers un 404, vers une erreur 500, etc.). Ici, c'est la magie des query selectors qui fait tout le boulot.

    Voyons par la pratique, comme on définit un listener :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    dispatcher.addEventListener('div.myClass', function(element, next, loaded) {
        // opérations sur l'élément
        // ...
     
        // passage au listener suivant
        next();
    });
    On y voit plusieurs choses :
    • 'div.myClass' : le sélecteur permettant de cibler tout élément à traiter via ce listener
    • element : un élément trouvé
    • next : une fonction servant à permettre aux autres listeners de faire leur traitement
    • loaded : booléen indiquant s'il s'agit d'un élément loadé via la lib ou non


    L'appel AJAX a été simplifié au possible... en effet, vous n'avez pas besoin de construire votre requête, il suffit de lui donner une ancre ou un formulaire et la bibliothèque se charge du reste :

    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
    // avec une ancre, donc envoi en GET
    fetcher(document.querySelector('a'))
        .fetch(dispatcher.dispatch);
     
    // avec un formulaire, en GET ou POST, selon la méthode renseignée sur le formulaire
    fetcher(document.querySelector('form'))
        .fetch(dispatcher.dispatch);
     
    // avec envoi des cookies
    fetcher(document.querySelector('form'))
        .credentials() // ce qui revient à appeler .credentials('same-origin') mais il est aussi possible de lui passer un 'include' pour du cross-origin
        .fetch(dispatcher.dispatch);
     
    // avec un ajout de header
    fetcher(document.querySelector('form'))
        .header('HeaderName', 'header value')
        .fetch(dispatcher.dispatch);
     
    // avec un ajout de valeur au body d'une requête POST
    fetcher(document.querySelector('form'))
        .body('fieldName', 'value')
        .fetch(dispatcher.dispatch);
    Bien entendu, il serait dommage de ne pouvoir appliquer cela qu'au HTML chargé en AJAX, c'est pourquoi il est possible d'appliquer les listeners enregistrés à la page chargée, de manière tout aussi simple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    dispatcher.populate(document);
    EDIT :

    La gestions des ancres/formulaires ne vous suffit pas ? il vous suffit d'ajouter/surcharger des fetchers... exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    fetcher.fetchers.button = function (button) {
        // fetcher.request(url, httpMethod, optionalBody)
        return fetcher.request(button.dataset.href, 'get');
    };
    Voilà

    À présent, la lib en question, pas encore publiée, la voici :

    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
    void function(t){var e,n,r,o,i,s,u,c
    e=t.document,n=t.fetch,r=t.FormData,o=Function.bind.bind(Function.call),i=o(Array.prototype.forEach),s=o(Range.prototype.createContextualFragment,e.createRange()),u=function(t){return Object.create(t||null)},c=function(){var t,e,r,o
    return t=function(){},e=[],e.n=function(){e.shift(),e.length&&e.fetch()},e.fetch=function(){var o
    if(!e[1])return o=e[0],n(o.q.u,o.q.o).then(r.r).then(s).then(r.z).then(o.d).then(e.n)["catch"](t)},r=function(t,e,n){var r,i
    return r=u(o),i=u(),r.o=i,r.u=t,i.headers=u(),i.headers["X-Requested-With"]="XMLHttpRequest",i.method=e,i.body=n,r},r.r=function(t){var n
    return n=e[0],n.q.response=t,t.text()},r.z=function(t){var n
    return n=e[0],n.q.response.fragment=t,n.q},o=u(),o.body=function(t,e){return this.o.body.append(t,e),this},o.credentials=function(t){return void 0===t&&(t="same-origin"),this.o.credentials=t,this},o.fetch=function(t){var n
    n=u(),n.q=this,n.d=t,e.push(n),e.fetch()},o.r=function(){e[0]&&e[0].q===this&&(e.unshift(1),e.n())},o.header=function(t,e){return this.o.headers[t]=e,this},o.option=function(t,e){return this.o[t]=e,this},r}(),t.fetcher=function(){var t
    return t=function(e){var n
    return n=e.nodeName.toLowerCase(),t.fetchers[n](e)},t.request=c,t.fetchers=u(),t.fetchers.form=function(t){return c(t.action,t.method,new r(t))},t.fetchers.a=function(t){return c(t.href,"get")},t}(),t.dispatcher=function(){var t,e,n,r,o,s
    return t=u(),e=u(),e.addEventListener=function(n,r){return t[n]=t[n]||[],t[n].indexOf(r)<0&&t[n].push(r),e},e.dispatch=function(t){e.onTimeout(t),setTimeout(e.populate,0,t.response.fragment,!0)},e.onTimeout=function(t){if(408===t.response.status)throw t.r(),Error()},e.populate=function(e,r){var o
    o=[],o.c=e,o.l=r===!0,i(Object.keys(t),n,o),o.n=s.bind(o),o.n()},n=function(t){var e,n
    e=this,e.s=t,n=e.c.querySelectorAll(t),i(n,r,e)},r=function(e){var n
    n=this,n.e=e,i(t[n.s],o,n)},o=function(t){this.push([t,this.e,this.l])},s=function(){var t
    t=this.shift(),t&&t[0](t[1],this.n,this.l)},e}()}(this);
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  2. #2
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    740
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 740
    Points : 805
    Points
    805
    Par défaut
    Petite démo très vite faite, désolé du peu pour l'instant, je manque de temps : https://jsfiddle.net/mmb3hhcL/
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  3. #3
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    16 939
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 16 939
    Points : 44 112
    Points
    44 112
    Par défaut
    Bonjour,
    je dirais quand même qu'avec un code non minifié et des commentaires tu aurais peut être plus de chance d'avoir des retours !

  4. #4
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    740
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 740
    Points : 805
    Points
    805
    Par défaut
    Merci de la suggestion, en fait, je l'ai mise en minifié parce que je ne l'ai pas encore publiée et que je cherchais surtout des avis sur l'usage (et, au passage, m'éviter 5000x l'explication sur le chat ^^')

    Mais, par la suite, oui, j'ai prévu de la publier et de faire une vraie doc et tout... juste que j'manque de temps, pour encore quelques jours.
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  5. #5
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    740
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 740
    Points : 805
    Points
    805
    Par défaut
    Bon, après quelques modifications, pour plus de simplicité, j'l'ai publié avec un beau readme, il ne manque plus que la référence qui viendra sous peu : https://github.com/Lcfvs/anticore
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

  6. #6
    Membre éclairé

    Femme Profil pro
    Experte JS / Conseillère en best practices / Chercheuse en programmation
    Inscrit en
    Octobre 2007
    Messages
    740
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Experte JS / Conseillère en best practices / Chercheuse en programmation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 740
    Points : 805
    Points
    805
    Par défaut Partage d'une discussion privée avec SylvainPV
    Bonjour à tous,

    Suite à un échange pas mal détaillé avec SylvainPV, je vous en fais part, car je pense que cela peut pas mal vous aider à comprendre en quoi cette lib est intéressante.

    Citation Envoyé par SylvainPV
    Si j'ai bien compris, anticore est dédié aux appels AJAX retournant du HTML. Je n'ai presque jamais utilisé AJAX de cette manière, j'utilise quasiment tout le temps des API REST retournant du JSON et je compose mes vues côté client avec le dernier framework à la mode. Requêter du JSON est souvent plus économe en bande-passante que du HTML, et gérer les vues côté client permet d'utiliser le site en offline et de faire de la compensation de latence (Optimistic UI). Donc je n'ai pas d'expérience sur de larges projets utilisant AJAX avec du HTML rendu côté serveur, le peu que j'ai fait était suffisamment simple pour tout gérer manuellement.
    En fait, il facilite le traitement d'HTML, SVG, XML, tout en simplifiant les appels AJAX et en brisant les chaines de callbacks/promises, afin de simplifier son code client.

    Il faut savoir que même si j'ai pensé à la solution des vues générées côté client, c'est parce que mes pages doivent être consultables sans JS et que, côté serveur, je génère tout en DOM, ce qui me permet d'avoir une très grande souplesse de rendu.

    Un chose que j'aime bien, aussi, avec cette approche, c'est que le back-end peut beaucoup plus piloter le côté client, sans trop prendre la main sur le côté front, puisque c'est lui qui va décider ce qu'il va injecter où (pas mal pratique, quand tu dois faire des versions spécifiques pour tel ou tel client achetant ton app).

    Citation Envoyé par SylvainPV
    Se baser sur des sélecteurs dans le contenu reçu pour lancer les post-traitements est original, mais j'ai du mal à voir les usecases. Par rapport à ce qui est décrit sur le topic et sur le github:
    - gérer les erreurs possibles ; est-ce que dans tous les cas on ne souhaite pas gérer manuellement toutes les erreurs ? Si un bout de HTML de ma page ne charge pas, c'est un gros problème ! Tout ce qui est erreur 500, 404 etc. je dois impérativement le gérer manuellement côté client pour faire le ménage de données locales, et ensuite ça se traduit dans 99% des cas par une redirection vers la page de login donc aucune importance si le JS de la page précédente ne fait rien.
    En fait, tout étant basé sur les sélecteurs, les messages d'erreurs aussi, y compris mes pages 404, 500, etc., donc en faisant des middlewares ciblant ces messages d'erreurs spécifiquement, je peux y déclencher facilement ce que je veux dessus.

    Citation Envoyé par SylvainPV
    - gérer un retour HTML différent: ça veut dire que pour une même URL le serveur renvoie une shape HTML différente ? ça me paraît peu naturel et délicat à gérer, comment s'assurer qu'on gère tous les retours possibles ? C'est une inquiétude que j'ai toujours eu avec le HTML rendu côté serveur, j'imagine qu'il y a des solutions et des bonnes pratiques à suivre
    Petit exemple assez simple, tu as un user qui submit son form, le serveur doit signifier à l'user qu'il y a des erreurs, eh bien, plutôt que de renvoyer le form avec les données réinjectées dedans et contenant les messages d'erreurs (comme on voit très souvent), mon serveur peut se contenter de ne renvoyer que les messages d'erreurs dont chaque noeud contient une classe ou un data-* indiquant à quel champ il se rapporte.

    Ensuite, j'ai mon middleware qui va se contenter de récupérer la cible et lui ajouter l'erreur.

    Il peut y avoir plusieurs erreurs pour un même champ et il peut y en avoir sur différents champs aussi, cela ne pose aucun problème.

    Citation Envoyé par SylvainPV
    Sur un plan purement technique:
    - je m'attendais à voir un MutationObserver pour surveiller les modifications du DOM et déclencher les callbacks des listeners ; a priori tout se fait avec un trigger manuel, c'est volontaire ? comment gérer les rendus de vues asynchrones ? si on doit déclencher le trigger via des Promise, on perd un peu l'intérêt de l'API orientée évènements
    En fait, le coup du MutationObserver, c'est ce que j'avais fait dans une de mes libs précédentes mais c'est beaucoup plus restrictif, consommateur en énergie (si tu veux pouvoir tout surveiller) et tu peux facilement avoir des problèmes de boucles infinies comme suggéré plus bas.

    Citation Envoyé par SylvainPV
    - est-ce que anticore sait si les éléments sont "consommés" ? c.à d. ne pas déclencher plusieurs fois le même listener tant que le sélecteur est trouvé dans le DOM. Est-ce qu'il y a un mécanisme de tag sur l'élément ou un store par référence ?
    - comment se prémunir des boucles infinies ? si un listener déclenche un trigger et qu'un élément matche à nouveau ce listener ? directement lié à la question précédente
    Il faut que tu comprennes bien que quand un middleware reçoit un élément, celui-ci n'est pas encore dans le document (sauf au populate), il est donc impossible qu'un élément passe 2x dans un même middleware.

    De plus, les requêtes AJAX, comme l'exécution des middlewares suit une queue, il y a donc un ordre d'opérations, selon l'ordre dans lequel tu as déclaré les choses, sans pour autant créer une chaine d'opérations inextricables. Il suffit donc de respecter une seule règle : 1 middleware, 1 responsabilité.

    Le reste, c'est à toi d'être aussi précis ou aussi générique que nécessaire, en fixant des sélecteurs plus ou moins stricts.

    Citation Envoyé par SylvainPV
    - un désavantage net en performances avec cette approche est que si des opérations de post-traitement sont à appliquer, on ne peut pas les paralléliser avec le temps de rendu de la vue puisqu'on se base sur le résultat du rendu (HTML produit) pour déclencher ces opérations. Donc perte en performances, sans doute négligée par le fait que le temps de parsing de HTML généré côté serveur est bien plus faible que gérer le rendu complet de la vue côté client.
    À mon sens, le JS étant mono-thread, cela ne fait aucune différence, hormis le gain de ne devoir générer toute sa vue, comme tu dis.

    Citation Envoyé par SylvainPV
    - autre désavantage que je vois mais qui concerne le rendu côté serveur en général, comment éviter le FOUC (Flash Of Unstyled Content) ? Si je dois initialiser un plugin jQuery une fois qu'une base HTML est chargée, en me basant sur le sélecteur avec anticore ça veut dire que l'utilisateur verra dans tous les cas pendant une fraction de seconde le HTML avant initialisation du plugin, donc FOUC. Avec des initialisations synchrones cela pouvait être résolu puisque tout était fait en un tour de boucle (append HTML + post traitement) mais si on se base sur le contenu du DOM résultant pour lancer le post traitement, ça veut forcément dire une boucle de rendu complète. Ou alors il faut une mécanique de cloak mais j'ai toujours trouvé que c'était une fausse solution.
    Ici, cela n'arrive jamais, puisque tes éléments ne sont injectés dans le document que lorsque tu le décides (via un middleware qui l'injecte).

    En résumé, ton code client ne se résume plus qu'à attacher des évents sur certains éléments, accessoirement ajouter quelques effets et injecter les éléments, ça ne requiert donc pas d'intelligence logicielle.

    Citation Envoyé par SylvainPV
    La plupart de mes retours viennent simplement du fait que je suis un convaincu du rendu client-side, j'en vantais déjà les mérites il y a 3 ans et depuis il y a eu Vue, React, Inferno et les dizaines d'autres frameworks de vue qui ont poussé le concept à fond. On est arrivé à un stade de maturité où les Hello World sont des SPA avec SSR et usage offline built-in, ce qui il y a quelques années nécessitait des mois de travail avec une équipe d'experts surpayés. Beaucoup se plaignent de la JS Fatigue mais pour ma part je trouve qu'on vit une époque formidable
    En fait, le rendu client-side, j'en étais convaincu aussi, à mes tout débuts en JS, je ne jurais que par ça mais, côté serveur, tu dois donc multiplier les approches pour gérer tous les besoins possibles (utilisateur sans JS, génération de PDF avec des éléments de l'identité visuelle du site, besoin de générer/lire du XML, génération d'emails, ...)

    Ici, j'ai un webservice que j'ai créé, avec lequel je communique en JSON et devant, j'ai des serveurs
    de rendu en DOM qui n'ont donc qu'une seule approche de rendu pour tout faire.

    Puisque cela répond certainement à plein de questions d'utilisateurs (potentiels) me permets-tu d'en coller une copie sur le topic, stp?

    -------------------------------------------------------------------------------

    Citation Envoyé par SylvainPV
    Pour permettre la consultation sans JS, j'utilise un framework avec Server Side Rendering (Nuxt en ce moment). Même chose pour la validation de formulaire, je fais toujours une double validation côté client et côté serveur, côté client pour permettre de valider à la volée et ne pas soumettre un formulaire invalide, et côté serveur pour la sécurité. Vu qu'on a déjà un Node.js pour le SSR, je peux ajouter un middleware Node qui utilise le même code de validation que côté client, ce qui m'évite de tout coder deux fois. Bien que sur les projets pro, il y a presque toujours une seconde phase de validation avec plus de règles fonctionnelles, auquel cas je fonctionne par code erreur en retour.
    Ici, j'fais aussi de la validation, côté client, mais comme la partie serveur est en PHP, je ne peux malheureusement pas réutiliser cette pre-validation côté serveur... j'aurais bien voulu mais quand le client impose le langage...

    Citation Envoyé par SylvainPV
    Ah, c'est très différent de ce dont j'ai l'habitude. Je ne mets jamais de HTML avec mes erreurs. Quand mon back-end retourne un code HTTP d'erreur, le body me sert uniquement à déboguer. Mais vu que côté utilisateur je fais de la compensation de latence, je me contente simplement de traiter l'erreur en retournant à l'état initial et en notifiant l'utilisateur avec un tooltip. A vrai dire, souvent mon back-end n'est même pas capable de traiter ou générer du HTML, je fais tout en JSON et je laisse le HTML au serveur SSR.
    Justement, faisant tout en DOM, j'peux facilement m'affranchir de cette double gestion

    Citation Envoyé par SylvainPV
    Le fait que le JS soit monothread n'empêche pas de paralléliser des opérations asynchrones, à moins que tout le traitement de la chaîne d'opérations soit entièrement synchrone ? Ce serait surprenant pour une API orientée événements. Mais oui, si on considère que tout post-traitement coûteux est fait uniquement après que tous les middleware anticore soient exécutés, la perte est tout à fait négligeable.
    En effet, le traitement de la pile de middlewares se fait de manière synchrone, afin d'éviter des effets de bord et de faciliter la compréhension de l'ordre de processus. Après, comme le traitement consiste juste à injecter des noeuds et faire des traitements mineurs dessus, c'est vraiment imperceptible pour l'utilisateur.

    Dans mon projet actuel, pour mes clients, on a même parfois l'impression que l'appel à une nouvelle vue similaire ne s'est pas fait, tellement c'est rapide (je passe pourtant par 2 serveurs et 2 db, le tout, généré en DOM qui a pourtant la réputation d'être trop lent), ce qui amène parfois à cliquer une 2ème fois pensant qu'il ne s'est rien passé.

    Citation Envoyé par SylvainPV
    Dans mes projets pro, dernièrement c'est l'architecture micro services qui est à la mode, on ne jure plus que par ça dans ma boîte. Tout est découplé, et j'ai droit à une instance Docker qui sert uniquement au front: un Node qui s'occupe du SSR et du file server. Pour tous les usecases que tu as mentionné (emailing, PDF etc.), à chaque fois c'est un nouveau micro-service qui s'en charge avec sa propre stack. C'est lourd à installer mais ensuite on voit tous les avantages: isolation des logs, redondance, mises à jour et patch ciblés, scalabilité horizontale etc. Tout ça me permet de me focaliser sur le front et tout gérer en rendu client-side, et au delà du fait que j'adore cette approche, c'est la seule solution pour l'usage offline et la compensation de latence (des features qui commencent à être bien appréciées de nos clients, avec toute la pub qu'on fait aux Progressive Web Apps ces derniers temps)
    Pour l'instant, je n'ai pas encore eu de projet nécessitant de l'usage offline bien que je songe à lui faire un plugin history pouvant éventuellement être relié à indexedDb.

    Citation Envoyé par SylvainPV
    Pas de souci pour copier coller nos échanges sur le topic, c'est vrai que le matching des sélecteurs avant insertion dans le DOM réel n'était pas très clairement expliqué et ça change beaucoup de choses dans la manière de penser l'appli.
    Prochaine MAJ de ma doc, je le préciserai explicitement car, en effet, peu de gens le voient ainsi et, pourtant, c'est ce qui simplifie tout
    Afin d'obtenir plus facilement de l'aide, n'hésitez pas à poster votre code de carte bancaire

    Mon GitHub

    Une alternative à jQuery, Angular, Vue.js, React, ... ? Testez anticore, en quelques secondes à peine !
    (Contributions bienvenues)

Discussions similaires

  1. Réponses: 2
    Dernier message: 09/10/2013, 14h24
  2. Cd de présentation pour mon groupe
    Par VinzFragments dans le forum Langages de programmation
    Réponses: 1
    Dernier message: 16/03/2008, 23h31
  3. Présentation de mon moteur 3D (NXEngine)
    Par funkydata dans le forum Projets
    Réponses: 151
    Dernier message: 28/04/2007, 00h52
  4. mon site 100 % flash donnez votre avis ! :)
    Par webox studio dans le forum Mon site
    Réponses: 19
    Dernier message: 11/11/2006, 01h39

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