mercredi 24 novembre 2010

Nouveau blog

Bonjour,
Enfin, j'ai passé pas mal de temps sur le développement de mon nouveau site internet, il est désormais accessible içi : http://www.leny-bernard.com

Je ne posterais désormais plus de contenu içi mais sur le nouveau site.
Au programme donc développement Symfony (bien sur), mais aussi développement iPad, GWT, jQuery etc.
à Bientôt !
Leny

vendredi 12 novembre 2010

Lancement de mon nouveau blog imminent

ANNONCE OFFICIELLE :

Ce blog va bientôt être obsolète !
Cependant, pas de panique !
L'adresse de mon nouveau blog sera dévoilée dans les prochains jours...

Au programme :
  1. plus de tutoriels
  2. plus d'articles
  3. mes flux de partage rassemblés
  4. mon CV
  5. mes portfolios
  6. un contact amélioré
A bientôt.
Leny

mercredi 3 novembre 2010

[SYMFONY] embedforms, embedi18n

Voici une petite fiche récap sur les formulaires imbriqués en symfony ainsi qu'un petit lien particulier pour ajouter des embedforms à la volée.

  1. Définition et exemple basique d'application 
  2. Personnalisation de l'affichage 
  3. Formulaires de traduction i18n
  4. Les formulaires embarqués ajoutés à la volée

  1. Définition et exemple basique d'application :
Un embedForm est un formulaire embarqué dans un autre formulaire. C'est très utile et par conséquent très utilisé dans symfony. Voici un exemple d'application :

Si vous avez besoin d'utilisateurs, vous avez du installer le plugin sfDoctrineGuardPlugin. Peu importe qu'est ce que vous utilisez pour gérer vos utilisateurs, le schéma de données est le même. Pour peu que l'on veuille agrémenter nos utilisateurs d'un avatar, leur souhaiter leur anniversaire, savoir leur genre (sexe), il va falloir rajouter des champs.
Ainsi on a 2 possibilités,

  • soit on modifie le schéma du plugin, mais c'est dommage car du coup, il va falloir le faire pour chaque nouveau site ou l'on aura besoin de faire ça.
  • soit, on modifie le schéma général en y ajoutant une table que l'on peut appeler Profile par exemple. On définit cette table avec les champs désirés (avatar, gender, birth_date, etc) et on définit une relation "one to one" avec la table définissant les utilisateurs. (On peut même aller plus loin dans la portabilité du code en créant directement un plugin personnel de gestion des profils; ainsi en 2 lignes de commandes, c'est gagné pour les nouveaux sites) 
On aura, en clair et dans le plus simple des cas, une table sfGuardUser et une table Profile

Donc, voici le nouveau schéma.yml pour la table Profile


Bon, première étape finit, mais pour l'instant, on a juste 2 modèles, reliés certes, mais qui définissent chacun 2 formulaires différents.
Il va falloir embarquer le formulaire d'utilisateur dans le formulaire profile (toujours dans un soucis de portabilité du code et dans l'optique de création d'un plugin, c'est du moins ma vision) 
Symfony permet de gérer facilement les formulaires embarqués. Il suffit de modifier la méthode configure du formulaire Profile et d'y ajouter ces lignes :



J'explique brievement mon code qui je pense est assez explicite :
Ici, je configure mon formulaire de profile. J'instancie un formulaire de la classe sfGuardRegisterForm et je lui passe l'utilisateur relié au profile grâce au schéma.

Ainsi, si j'affiche $form, j'aurais les 2 formulaires imbriqués, ne faisant qu'un seul et même formulaire pour l'utilisateur.

Je vous ai montré aussi comment on peut modifier de la même façon qu'un formulaire normal un formulaire embarqué, par exemple les labels.

   2. Personnalisation de l'affichage

L'exemple précédent ne me convient pas totalement, si vous l'avez testé chez vous, vous savez probablement de quoi je parle, l'affichage n'est pas correct.

Premièrement je veux mixer totalement les 2 formulaires. Pour l'instant ca m'affiche les 2 formulaires dans un même tableau, cependant,le formulaire sfGuardRegisterForm est affiché avec un label : "Utilisateur :"; ce qui décale les champs d'une colonne. Ce n'est pas ce que je veux. Aussi, je peux modifier l'affichage en allant modifier le fichier correspondant, soit _form.php et au lieu d'afficher $form simplement, on va afficher séparément les éléments.

Ca donne ca :


Ca nous affiche correctement le formulaire désiré, parfait.

    3. Formulaires de traduction i18n

Parfois, vous aurez à faire de la traduction de contenu, par exemple, si vous faites un blog pour plusieurs langues, vous allez créer le modèle Article et vous allez le configurer comme ci-dessous afin qu'il soit traductible dans les langues spécifiées.

Un petit build --all créé comme à son habitude les classes de formulaire et de modèle. Ainsi si on va voir dans le dossier lib, on peut voir la classe ArticleTranslationForm.class.php

On n'y touche pas, le but du sujet est juste de savoir comment l'afficher, on va droit au but, dans la méthode configure du formulaire, on va insérer nos formulaire de traduction comme suit :



Ca affichera ainsi, dans le formulaire de création d'article, les deux formulaires de traduction, c'est aussi simple que ça mais quand on ne sait pas, on ne peut pas l'inventer ;)

    4. Les formulaires embarqués jaoutés à la volée


http://www.nacho-martin.com/dynamic-embedded-forms-in-symfony

Site d'un blog technique intéressant : élao

Des articles très intéressants, touchant à ce que j'aime, symfony, linux, applications iphone et ipad ! J'aime
http://www.elao.org/

lundi 1 novembre 2010

[SYMFONY] SQLSTATE[42s22] Erreur 1054 = SOLUTION

Bonjour,
Si vous tombez sur ce message, c'est probablement qu'il vous est arrivé cette erreur :


J'ai pour ma part eu ce problème et j'ai réussi à m'en sortir malgré le fait que je ne comprenne pas vraiment la base du problème. Aussi, ne maitrisant pas cette erreur, je ne vous donne pas la solution mais UNE solution qui a fonctionné pour moi mais qui ne fonctionnera peut être pas chez vous.

Voici mon contexte :

J'utilise :


  • Linux Ubuntu 10.10
  • PHP 5.3
  • Apache 2
  • Mozilla Firefox & Google Chrome
  • Doctrine
Jusqu'içi, rien d'étonnant. Le problème surgit lorsque je créé des tables i18n, c'est à dire lorsque je fais ce type de schema.yml

actAs:
    I18n:
      fields: [title]


L'erreur n'arrive pas toujours. Très étonnant, parfois, sur la même application, pour 2 pages quasiment similaires, l'une déclenche l'erreur et l'autre non.

C'est à rendre fou !

J'ai remarqué que cela plantait sur des requêtes tapant sur des tables traduites. C'est comme si Doctrine ne faisait pas la jointure et ne trouvait pas la traduction. J'ai trouvé par hasard un moyen pour "colmater" la brêche, le voici :

Il m'est parfois apparu cette erreur sur une requête ou j'écrivais le nom du modèle en minuscule.

J'ai du modifier le nom de modèle en insérant une majuscule en première lettre comme ceci.

J'ai eu le problème inverse, ayant mis le nom de la classe avec la première lettre en majuscule, j'ai du mettre tout en minuscule. Je ne comprends pas forcément de quoi ca peut venir et surtout si vous pensez savoir, dites le moi, j'aimerais beaucoup comprendre cette histoire là.
En tout cas, ca fonctionne pour moi, j'espère que ca fonctionnera pour vous aussi.

mercredi 6 octobre 2010

[SYMFONY] Envoyer des emails

Petit article juste pour présenter les emails dans Symfony, j'ai pu lire beaucoup de choses, bien et moins bien sur les email dans Symfony, voilà ce que j'en ai retenu :

  • SWIFTMAILER est intégré à Symfony depuis sf 1.3
  • Pour créer un email, il faut d'abord instancier un mailer, cela se fait avec cette commande :

    $mailer = sfContext::getInstance()->getMailer();
  • pour construire l'email, on utilise la méthode compose() du mailer

    $message = $this->getMailer()->compose('email@destination.com, 'email@source.com', 'mon OBJET', 'mon contenu');

    On peut aussi faire des beaux emails en utilisant des vues de symfony en utilisant la méthode setBody() du message

    $message->setBody($this->getPartial('myPartialView',array("variable1"=>"1")), 'text/html');
  • Pour envoyer ce message, on utilise la méthode send() du mailer en lui passant en paramètres le message, l'email destinataire, l'email de réponse

    $mailer->send($message, $emailTarget, $emailReply);

Pour plus d'informations sur les emails avec Symfony comme par exemple la configuration des envois de mail, le mode de distribution, d'envoi etc, n'hésitez pas à aller voir le livre de Symfony "The more with Symfony, page 4"

[SYMFONY][JQuery] Gérer ses formulaires en Ajax, validation soumission, etc !

Bonjour,

Bonne nouvelle : Cet article est très utile :)
Il va nous permettre de traiter un formulaire à la volée et de récupérer les erreurs qu'un mauvais remplissage pourrait renvoyer.

Imaginons que l'on veuille ajouter un commentaire à un article de blog.
On affiche notre article puis en dessous, on affiche un lien pour commenter l'article.
Le formulaire de commentaire doit connaitre implicitement le sujet à commenter.
Aussi, il faudra :

1. modifier le formulaire de commentaire pour passer l'id de l'article en douce (grâce au widget sfWidgetFormInputHidden

/lib/form/Doctrine/CommentForm.php
public function configure()
  {
      $this->widgetSchema['model'] = new sfWidgetFormInputHidden();

      $this->getWidgetSchema()->setLabels(array(
          'author'=> "Pseudo :",
          'content'=> "Message :",
          ));
  }
 2. passer l'id de l'article lorsqu'on charge le formulaire
On peut soit afficher le formulaire dès le chargement de la page, soit en Ajax, faisons le en Ajax, ce serait trop facile ;)

On va donc utiliser JQuery. Installons déjà le plugin sfJqueryReloadedPlugin
Une fois que c'est fait et que l'on a bien inclut le helper jQuery, on peut se créer dans un fichier js que l'on va inclure (je vous fait confiance là dessus) une fonction qui sera déclenchée lors du clic sur le bouton commenter.

/app/frontend/blog/show/myArticle

<div>
    <h1>
        <?php echo $article->getTitle(); ?>
    </h1>
    <p><?php echo $article->getContent(); ?></p>
</div>
<input type="button" value="Commenter" onclick="comment(<?php echo $article->getId(); ?>)"/> 
<div id="commentForm"></div>

/web/js/main.js

function comment(arg){
    $.post("/comment/new",
    {id: arg},
    function(data)
    {
        $("#commentForm").html(data);
        $("#commentForm").slideToggle();
    });
}
Jusque là, on est pas trop désorienté, c'est juste la facon de charger le formulaire de commentaire.
Il faut donc maintenant créer l'action comment dans le module blog :

/app/frontend/modules/comment/actions/actions.php

public function executeComment(sfWebRequest $request) {
        $comment = new Comment();
        $comment->setArticleId($request->getParameter("id"));
        $this->form = new CommentForm($comment);
    }
Vous l'avez surement compris, dans le code précédent, on créé un objet commentaire pour pré-remplir avec l'id de l'article.  On charge ensuite commentSuccess.php. Crééz le comme vous voulez, je vais personnelement aller au plus simple pour bien montrer la facilité du processus.


/app/frontend/modules/comment/templates/newSuccess.php

<?php use_helper('jQuery'); ?>
<?php echo jq_form_remote_tag(array(
    'update'   => 'commentForm', /* représente l'id de la div que l'on doit mettre à jour */
    'url'      => 'comment/'.($form->getObject()->isNew() ? 'create' : 'update'),
)); ?>
  <?php echo $form; ?>
<input type="submit" value="Commenter"/>
</form>
On réinclut le helper de jQuery. Pourquoi ? car sinon, la vue, isolée car chargée en ajax est incapable de trouver la fonction jq_form_remote_tag.
On passe 2 paramètre à cette fonction : url, qui désigne l'action que va éffectuer le formulaire et update qui définit dans quel id de la page l'action ajax va afficher la réponse. Içi, dans la div d'id commentForm.

Désormais, il faut dire quel vue on va afficher lorsque cela sera validé. Le formulaire, une fois validé et enregistré raffiche le formulaire pour l'éditer, fonctionnement par défaut mais qui ne nous convient pas. On peut pour changer cela en ajoutant un test dans la template newSuccess.php.
Grâce à l'attribut $form->getObject()->isNew() des formulaires, on peut en déduire le comportement que l'on doit adopter, soit afficher le formulaire (comportement 1) soit afficher le commentaire (comportement 2).


/app/frontend/modules/comment/templates/newSuccess.php

<?php if($form->getObject()->isNew()){?>
    
        <h3><?php echo __("Ajout d'un commentaire :");?></h3>
        <?php include_partial('form', array('form' => $form)) ?>
    
<?php }else{
    include_partial("show",array("comment"=>$comment));
}?>



Voilà qui est fait. Maintenant. Le formulaire est en Ajax, s'il y a des erreurs dans le remplissage du formulaire, les erreurs seront affichées puis lorsque cela sera valide, le nouveau commentaire sera affiché comme les autres commentaires grâce au fichier _show.php que vous aurez créé selon votre envie de mise en forme des commentaires.

mercredi 29 septembre 2010

Le ShowCase de Google

Ceci est le showcase de l'API Ajax de Google... elle regroupe beaucoup plus que du simple Ajax, elle intègre de la traduction grâce au moteur de google translate, gmaps, gearth, blogger, des graphiques, etc etc.

Intégrable facilement sur n'importe quel site, faites vous plaisir, il suffit de cliquer sur l'exemple voulut puis de choisir ViewDocs en haut à droite.
On peut même et ca c'est pas mal, changer le code de la vue du haut puis de générer le résultat en bas.
:)

http://code.google.com/apis/ajax/playground/

samedi 18 septembre 2010

[GWT] Problème de relève d'évenement

En GWT, pour relever des évenements, on utilise des handlers.
Parfois, comme dans la classe Slider de GXT com.extjs.gxt.ui.client.widget.Slider, des méthodes éxistent déjà pour déclencher des actions lors d'un évenement particulier.

Déclenchons par exemple un évenement au clic sur notre slider.
Si on instancie un slider, l'auto completion d'Eclipse ne nous montre que les méthodes publiques. Or si on regarde dans la doc, il existe un bon nombre de méthodes permettant de faire ca : la méthode OnClick par exemple.

Mais comment l'obtenir ? Je me suis prit la tête pendant une après midi pour voir que ces méthodes sont protected... Des méthodes ?? Protected ?? mais pourquoi ? Et bien j'imagine que vu que c'est destiné à être des scripts JavaScript, cela ne doit être fait qu'une fois, lors de son instanciation... à vérifier, n'hésitez pas à me dire si vous avez une meilleure explication.

En tout cas, vous l'avez compris, ce n'est pas perdu, elles sont là et on peut les utiliser.
Au lieu d'instancier un slider "normalement", en utilisant le constructeur normal :
On peut l'instancier et surcharger les méthodes que l'on veut utiliser.

jeudi 16 septembre 2010

[OBJC] Découverte de l'Objective C / 31 jours = 31 applis, on va tenter de garder le rythme !

Je me suis mis au developpement iPhone depuis quelques jours.
Pour se faire, j'ai installé un vm Mac OS X 10.6.2 puis j'ai installé XCode.
Ensuite j'ai joué avec et j'ai trouvé ça très marrant.
J'ai ensuite eu la chance d'obtenir un bouquin sur le Cocoa.
Ca explique le fonctionnement et les analogies entre l'objective C et le Java que je connais mieux.

Une fois les bases assimilées, le code objective C m'est apparu plus clair.
Je me suis ensuite lancé dans la création d'un TP personnel : une calculatrice.

Plutot que de refaire le tuto, je vous conseille de suivre celui-ci que j'ai trouvé très complet. J'ai laissé un commentaire sur son site pour lui proposer une amélioration au niveau de son code. Ceci dit, vous pouvez suivre son tuto les yeux fermés, ca marchera :)

Voilà ma calculatrice si ca peut vous motiver :)

Je vous conseille ce site, très bien écrit et assez récent (par rapport à aujourd'hui)
TUTO CALCULATRICE

Faites vous plaisir.
autre ressource

mardi 14 septembre 2010

[GWT][GXT] Les grids

En GXT, on a souvent le besoin de mettre en forme des données. J'ai eu besoin de le faire, en me baladant sur le showcase de Sencha, j'ai vu le plugin exapander pour les grid. Un très beau rendu qui permet d'afficher des compléments d'informations pour une liste d'objets donnée.
Ca donnera ça :



On part de notre modèle pour aller vers la vue.

 Voici notre modèle : Student =

On déclare 3 attributs pour faire simple et un constructeur recevant nos 3 attributs.
On créé ensuite une interface héritant de BeanModelMarker

Ne surtout pas oublier l'annotation @BEAN({package}.{Class}.class), elle est très importante dans la liaison entre la classe Student et cette interface.

Maintenant on passe du côté Vue

Là ou on veut afficher notre grid, on créé une liste de Student, appelée students :

On se créé un magasin puis pour chaque Student de notre liste students, on génère le BeanModel associé. Ainsi, nous avons un store contenant des BeanModel :
On créé ensuite un XTemplate (KESAKO ? c'est ce qui sera affiché lorsque l'on cliquera sur le petit plus dans notre grid, on peut ne pas le faire si on veut juste une grid standard) :
Maintenant, il faut créer les colonnes. C'est là que c'est Magique.On créé une liste de ColumnConfig, on y ajoute notre plugin expander qui nous permet d'afficher des info supplémentaires selon le précédent XTemplate puis on créé autant de nouveau ColumnConfig que l'on veut de colonne.

 
2 méthodes très importantes ci-dessus,

colum.setHeader("Prénom");
colum.setId("firstName");

La méthode magique est cette dernière. Ce qu'on lui passe => firstName est, vous l'avez peut être déjà remarqué, le nom d'un des attributs de mon objet Student. Il faut retourner dans notre modèle Student pour générer tous les getters dont nous aurons besoin. ici firstName et LastName Student.class.php


Maintenant c'est quasiment fini, il faut créer le fameux Grid en lui passant notre liste de (Student)BeanModel et notre ColumnModel cm.


Voilà, c'est fini et normalement, je viens de le tester, ca fonctionne niquel !
Enjoy ;)

lundi 13 septembre 2010

[GWT] Les Layouts

Première étape de la création d'une application GWT, l'interface utilisateur.
En GWT, on utilise les Layouts (Patron, MasterPage).

En gros on créé l'architecture de la vue en créant un layout général.

Toujours avec la fameuse librairie GXT (cf les précédents tuto), on va réussir à faire un super truc très rapidement.

Deux trucs à savoir :

1) Il existe beaucoup de types de layouts. Nous utiliserons le type de layout utilisé dans l'exemple du showcase de Sencha BorderLayoutExample car il est très performant, assez rapide de mise en oeuvre et déjà stylé et largement parametrable, c'est probablement au moment ou je parle le plus puissant layout de GWT.
2) Pour redimensionner automatiquement notre layout lorsqu'on redimensionne le navigateur, il faut utiliser l'objet VIEWPORT

Pour intégrer Viewport à notre exemple de borderlayout, on va importer Viewport et l'instancier. Il faudra toujours considérer ViewPort comme la vue globale.

J'ai renommé l'exemple BorderLayoutExample en GeneralLayout.
J'ai enlevé la classe mère LayoutContainer de la déclaration de classe, l'objet ViewPort héritant déjà de LayoutContainer

Je ne vais pas revenir sur les exemples de Sencha, c'est assez explicite, je vous montre seulement ce qui change dans le fichier GeneralLayout à la fin, lorsqu'on ajoute du contenu à une zone du layout :


Il reste une chose à faire maintenant, utiliser viewport comme vue globale dans le onModuleLoad()


Ainsi, on peut maintenant redimensionner notre navigateur, le layout se redimensionnera instantanément.
Plutot Génial hein ?

jeudi 9 septembre 2010

[GWT] Changer les ports des applications 8888 et 9997

J'ai vu que personne sur le web ou presque n'en parlait, par défaut, une application GWT est lancée sur un serveur d'application embarqué JETTY sur le port 8888. Lorsque vous développez vos applications, il faut installer un plugin dans votre navigateur qui va communiquer avec votre application codée en JAVA et tout retranscrire la partie client en JavaScript.

Pour développer plusieurs projets en même temps, il faut donc paramétrer 2 ports.
Pour accéder aux paramètres de son application, il faut faire un clique droit sur son projet et sélectionner Run As > Run Configurations :


On arrive dans la configuration de notre application GWT.
Il faut changer à 2 endroits des ports.
Le premier, celui de JETTY. Par défaut, il est égal à 8888, j'ai donc modifié le port en 8888 vers 8890, on peut aussi bien choisir de cocher l'option qui permet de toujours choisir un port non utilisé. C'est pratique, autant s'en servir.



Deuxièmement, il faut changer le deuxième port en se placant dans l'onglet Arguments et en ajoutant l'option -codeServerPort 9998, par exemple pour le numéro de port



Ainsi, vous pouvez avoir plusieurs projets accessible grâce à la combinaison des 2 ports.
Voilà pour la petite astuce.

vendredi 3 septembre 2010

[GWT] Découverte de la librairie GXT

Sencha propose une librairie EXJ GWT appellée GXT.
On peut y accéder ici : http://www.sencha.com

On télécharge la dernière version stable, au jour d'aujourd'hui, septembre 2010, c'est la version 2.2

On part du principe qu'on a déjà créé un projet GWT.

Dans notre projet GWT on créé un dossier lib puis un dossier gxt-2.2.0 à la racine du projet, on y extrait le fichier gxt.jar présent à la racine du fichier téléchargé.

Il faut maintenant inclure le jar à notre projet. Dans eclipse, il suffit de faire un clic droit sur notre projet -> proprietés (ou Alt + Entrée) puis de choisir "Include Jar"
On va chercher le fichier gxt.jar qui se trouve dans ~/WORKSPACE/PROJECT/lib/
gxt.jar

Il apparait ainsi dans la liste des librairies ajoutées au projet.

Il faut ensuite ajouter les ressources au projet (icons,images, model). Pour faire cela, il faut cliquer sur l'onglet Source (des proprietés du projet) puis choisir "Add Folder". On va chercher le dossier lib/gxt-2.2.0/samples/resources/src

Il faut maintenant copier le dossier ressources présent dans le fichier que vous avez téléchargé gxt-2.2.0 et vers le dossier war.
Il faut maintenant modifier le fichier html pour inclure le fichier css de gxt-all.css
    <link type="text/css" rel="stylesheet" href="resources/css/gxt-all.css" />
Il faut maintenant référencer gxt en ajoutant ces lignes dans le fichier de config TestGXT.gwt.xml :
  <!-- Other module inherits                                      -->
  <inherits name='com.extjs.gxt.ui.GXT'/>
  <inherits name='com.extjs.gxt.samples.resources.Resources'/>
GXT est désormais installé.
On peut désormais aller sur le show case de sencha pour faire nos emplettes, Voir source, copier coller et remplacer la regex suivante \s[0-9]+.\s par "".

Vous pouvez télécharger le projet minimal ici :


vendredi 27 août 2010

[GWT] Tutoriel AJAX

Bonjour,                                                                                        GWT V2.0.4
Pour commencer à toucher au modèle MVC, on va se pencher sur la réalisation d'une calculatrice respectant le modèle MVC et  appelant le serveur pour ce qui est du calcul de factorielles.
Dans un premier temps, on va faire une calculatrice non ajax, seulement coté client.
Je vous conseille de faire maintenant un petit diagramme de classe faisant apparaitre correctement les cotés Model, View et Controller ainsi que les interfaces implémentées ou les classes mères.
On rajoutera dans la partie 2 les appels RPC (AJAX)
Voici mon diagramme de classe, celui qu'on utilisera lors de ce tuto :


On va donc créer une nouvelle application GWT, (voir le tuto précédent) puis on va créer de nouveaux packages à ceux déjà existant.

Ainsi dans le package com.client, on va créer 3 packages : model, view, controller.

Cela nous créé notre modèle MVC.
On va d'abord créer le MODELE Calculator.
1) Clic droit sur le package, new-> class; entrer Calculator comme nom de classe puis valider.
2)  sur notre schéma on a déclaré 4 constantes, définissant les modes de calcul disponible de la calculatrice. On a aussi une "proprieté" mode qui définit le mode courant.
La proprieté oldValue est le résultat de l'opération précédente.
La proprieté currentValue correspond au nombre dernierement ajouté.
La proprieté saveCurrentValue est un booléen qui va gérer l'ajout ou le remplacement des chiffres à l'écran. Ainsi, la première touche, au départ et après une opération, remplacera le résultat et le booléen passera à false jusqu'à ce que l'on clique sur un opérateur (+ - * / = ).

3) Les méthodes ! On déclare un constructeur Calculator(); Rien de méchant, le constructeur va servir à instancier (créer) un nouveau calculator.
On va ensuite déclarer un méthode appendDigit(int value) qui va être appellée lors du clic sur un bouton numérique (0-9) et va servir à remplacer le résultat à l'écran ou à le mettre à la suite (en fonction du booléen saveCurrentValue).
La méthode clear() va servir à purger notre calculatrice, c'est litteralement un remise à zéro RAZ, déclenchée lorsqu'on clique sur le bouton C.
La méthode compute() va calculer notre opération. En fonction du mode (ADD=>0,SUBSTRACT=>1,MULTIPLY=>2,DIVIDE=>3)
la méthode getOldValue() est juste un accesseur, pas besoin d'en parler plus longuement.

On va maintenat créer la VUE CalculatorWidget.
Celle-ci va nous permettre de générer notre clavier, notre écran, bref notre calculatrice coté utilisateur.

1) On a une proprieté screen qui va définir l'écran dans lequel l'utilisateur verra affiché la réponse.
2) On ajoute ensuite les méthodes. On a une méthode pour afficher quelque chose à l'écran, une méthode pour créer une touche : createButton(), une autre pour le clavier : createKeyboard() qui va utiliser la méthode createButton().
Mais avez vous remarqué que notre CalculatorWidget hérite de SimplePanel.
Notre widget sera donc un SimplePanel et on veut que lorsqu'on le charge, on génère un VerticalPanel dans lequel on ajoute screen (TextBox) qu'on désactive pour ne pas qu'on puisse y taper n'importe quoi, et qu'on génère un clavier pour pouvoir demander un calcul.
On ordonne ces actions dans la méthode @override onLoad()

La fonction display() est bateau, pas besoin d'un dessin, je la met quand même pour plus de clarté.

On va enfin créer le CONTROLLEUR ButtonHandler.


1) Le controlleur est la pièce directrice de votre programme. C'est ici que l'on va créer notre modèle et notre vue et que l'on va effectuer les actions au clic des boutons.
Ainsi, on va faire implémenter l'interface ClickHandler et on va donc implémenter l'action onClick() afin de définir le comportement du programme et donc calculer les opérations et recharger l'affichage de l'écran.

On pourrait presque penser que c'est fini et c'est presque vrai !

Il faut maintenant changer ce que fait le programme, le main si vous venez du Java. En GWT, vous l'avez compris, on appelle ça le onModuleLoad() :


                                                                                                                          
ACTION AJAX
                                                                                                                          
On va essayer d'expliquer le plus facilement possible les concepts de base. 
Pour intégrer de l'AJAX dans notre calculatrice, on va ajouter une nouvelle fonctionnalité : la factorielle.
C'est peut être pas très clair pour ce qu'est la factorielle alors je vous le dis la factorielle de 5, c'est 1x2x3x4x5 donc c'est pas forcément très dur mais ca monte très vite dans des nombres énormes.

GWT fournit un framework complet reposant sur un modèle entièrement asynchrone.
Voilà comment s'articule le moteur RPC :




L’interface de service est le contrat de notre service. C’est l’ensemble des opérations que nous proposons de distribuer. Il existe deux types d’interface.
• L’interface synchrone (MyService/FactoService) : c’est cette interface qui est utilisée par notre implémentation sur le serveur. Elle est générée en JavaScript lors de la phase de compilation et se
situe côté client du fait de sa dépendance avec d’autres éléments côté client.
• L’interface asynchrone : dans GWT, toute requête Ajax s’effectue de manière asynchrone. C’est cette interface qui est concrètement utilisée lors de l’invocation par le code client.


1) On va donc créer notre interface de synchrone. On va la nommer FactoService et la faire étendre de RemoteServiceServlet
    On déclare la méthode pour calculer la factorielle. On va l'appeller factoServer(int n)
    Il faut ensuite assigner à notre servlet un alias en ajoutant @RemoteServiceRelativePath("facto") au dessus de la déclaration de l'interface


2) Puis l'autre interface, l'asynchrone. On va la nommer, et c'est obligatoire de la nommer comme ceci NomDeLInterfaceSynchrone+Async, ce qui donne FactoServiceAsync




3) Comme vous le voyez sur le schéma au dessus, on doit générer un proxy. Le Proxy va servir à communiquer entre le client et le serveur. Sur le schéma, il y a marqué "Généré", Java nous permet de créer dynamiquement des classes. On appelle la méthode create de la final Class GWT en lui passant le nom de l'interface FactoService et le type FactoServiceAsync. Il faut mettre cette ligne de code dans le modèle ou l'on veut éffectuer l'action AJAX (on peut cependant le mettre ailleurs si on en a besoin ailleurs, tant que ca respecte le modèle MVC).
Voici la fameuse ligne de code :



4) Le mécanisme RPC est quasiment fini coté client. Il faut maintenant créer la méthode dans notre modèle. Je vous rappelle le but de l'action AJAX, c'est trouver la factorielle d'un nombre.
Donc dans notre modèle Calculator, on va ajouter une méthode askFactoToServer(int n)


On a bouclé tout ce qui se trouve du coté client.
Une classe reste désormais à implémenter, la classe coté serveur.

5) Sur le schéma, on voit la classe MyServiceImpl.java, c'est la classe coté serveur qui va nous calculer notre factorielle.



on ajout @suppressWarnings("serial") pour éviter un avertissement lié à la sérialisation

Et voilà ! Profitez bien !

mardi 17 août 2010

[GWT] Tutoriel de découverte

Me voilà dans un nouveau stage !
Je travaille pour un professeur qui souhaite créer un logiciel en ligne d'exercice de maths, de suivi des élèves et de gestion de compétences.
On va normalement travailler avec Google web toolkit !
C'est avec ce toolkit que Google code ses applications et le concept est assez simple et interessant :
- Ce n'est que du JAVA !
Je ne sais pas si c'est une bonne nouvelle mais c'est comme ca :D

Le fonctionnement est donc que l'on code tout en Java en utilisant les librairies qui conviennent. On ne touche normalement jamais à une ligne de code js et c'est completement objet.
J'ai donc installé ça ce soir et ca me donne très envie aussi je vous le recommande, je vais m'y mettre serieusement là et je posterais quelques tutoriels à ce sujet.
Ci contre un premier screen de ce qu'un projet neuf donne :

Je vous propose maintenant un petit tuto de découverte de GWT :
c'est fortement inspiré du tuto que propose google ici : StockWatcher
                                                                                                                             
Il faut tout d'abord installer eclipse si vous ne l'avez pas déjà fait.
1. téléchargez la dernière version d'eclipse sur http://www.eclipse.org/downloads/
2. ensuite allez dans Eclipse -> Help -> New Software
Une fois dans New Software, on ajoute cet url : http://dl.google.com/eclipse/plugin/3.5 qui contient les sources de GWT et le plugin GWT pour eclipse
3. Cochez  tout
4. Continuez l'installation jusqu'à la fin, logiquement pas de problème sinon suivez ce qu'ils disent, puis redémarrer.
5. De nouvelles choses apparaissent désormais , notamment 3 icones avec le g emblématique de google.
6. Eclipse vient d'installer GWT et être maintenant prêt à l'utiliser.
                                                                                                                             
Je vais fonctionner ici en captures d'écran pour le code pour ne pas que vous copiez (bêtement) le code car c'est incomparable la différence entre écrire le code et le copier pour la compréhension.
On créer maintenant une nouvelle application web google. C'est assez instinctif, c'est cette icone . 
On renseigne le nom du projet :stockWatcher et le package, habituellement, je mets juste com
Ca génère donc le hello world, c'est assez marrant, ca montre un peu comment ca s'organise mais ca ne sert pas à grand chose.


On va maintenant dans war et on ouvre le fichier stockwatcher.html (ca dépend du nom que vous avez pris pour votre nom de projet, j'ai moi pris "stockwatcher") puis on fait le ménage, rien qu'en enlevant les commentaires, on arrive à un truc propre et simple :
<!doctype html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <link type="text/css" rel="stylesheet" href="StockWatcher.css">
    <title>Web Application Starter Project</title>
    <script type="text/javascript" language="javascript" src="stockwatcher/stockwatcher.nocache.js"></script>
  </head>
  <body>
    <h1>StockWatcher</h1>
    <div id="stockList"></div>
    <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
  </body>
</html>


GWT utilise les id pour dire ou il veut afficher ses élements. Ainsi, on a fait une div avec l'id stockList vide mais dans le but d'y mettre notre contenu.
on ouvre maintenant com.client.StockWatcher.java
On a notre classe, déclarée comme ceci : public class StockWatcher implements Entrypoint{ } 
Je vous rappelle qu'on déclare ce dont on a besoin directement dans la classe.


Dans ce tuto, on veut pouvoir ajouter en Ajax de nouvelles entrées dans un tableau, on va donc avoir besoin :

  • d'un tableau (flexTable)
  • d'un input text (TextBox) 
  • d'un label pour indiquer la date de dernière mise à jour (Label)
  • d'un bouton (Button)
On a aussi besoin d'un panel, c'est typique de Java, considerons le simplement comme un socle.

  • un panel pour l'affichage (VerticalPanel) et un panel horizontal pour l'ajout d'entrées
Mais tous ces éléments nécessitent l'import de certains fichiers.
On met ces import de librairies tout en haut du fichier.
Donc on met ca juste en dessous de la déclaration de la classe.



Le point d'entrée du programme, c'est la fonction public void onModuleLoad(){ }

C'est notre "main" pour ceux qui le cherchaient...
On commence par créer notre tableau dans lequel on va stocker les données, c'est le FlexTable stocksFlexTable :

Cela fera 4 colonnes.

On va ensuite y mettre l'input text et le bouton pour pouvoir ajouter des nouvelles entrées dans l'horizontalPanel addPanel:


Maintenant on met dans le mainPanel le tableau, le panel d'ajout d'entrées ainsi que notre label de mise à jour :


Vient ensuite une chose très interessante, la notion de RootPanel.
Le RootPanel, c'est notre page ! On ne la déclare jamais, elle est omniprésente et sert à communiquer entre notre classe et notre fichier html.

Ainsi, on va mettre notre mainPanel dans la div d'id stocksList comme ceci :



Maintenant, si on appuye sur F11 dans eclipse, on build notre projet et on peut le visualiser dans notre navigateur préféré (sauf au moment ou j'écris sur chrome sous ubuntu, c'est un comble !! Google quand même :S )

Mais pour l'instant, rien ne se passe en cliquant sur notre bouton et le tableau est vide.
Il faut créer une action sur le clic du bouton pour pouvoir envoyer le contenu de l'input text vers le flexTable.

Cela se fait avec la méthode addClickHandler de notre objet addStockButton.


Vous voyez ici que l'on fait appel, dès que l'on clique sur le bouton addStockButton, la fonction addStock();  Or cette fonction n'éxiste pas et eclipse va vous le faire remarquer.
Eclipse est sympa ! Il va même vous proposer de la créer à votre place.

Imaginons qu'on ne veuille pas ajouter 2 fois la même chose dans notre FlexTable et seulement des entrées de plus de 4 caractères et sans caractères spéciaux.
On créé une liste des entrées qu'on ajoute et à chaque fois on va vérifier qu'il n'éxiste pas dans notre liste avant de l'ajouter dans le flexTable et on teste le contenu avec un REGEX avec la fonction matches("^[0-9A-Z\\.]{1,10}$")

On affiche un message d'erreur avec l'objet Window, accessible grâce à l'inclusion :
import com.google.gwt.user.client.Window;

On oublie pas d'instancier notre liste stocks ArrayList, juste en dessous des autres déclarations
Puis on écrit notre fonction. Ca donne ça :

On peut enfin ajouter des choses dans notre flexTable ! Enjoy