Table des matières

TD n°6 : Conception de logique applicative

Objectifs

Situation initiale

Erratum :
Pour continuer le TD n°6 et éviter de rencontrer à nouveau les bugs de cette semaine, pensez à mettre à jour les fichiers de votre projet avec ceux contenus dans l'archive :

Conception des vues

Villes

Objectifs

Il s'agit de donner à la liste des villes le comportement suivant :

Ajout de la sélection au clavier

la méthode addSelector permet d'ajouter le contrôle clavier sur la liste : le code de touche 113 correspond à la touche F2, et permet l'édition d'une ville : voir http://tutorial.kobject.net/java/ajaxinclude/keyboard

{#func:this.addSelector(113)#}
{#func:this.setEditable(true)#}
{#mask:<td>{cp}</td><td>{ville}</td>#}
{#mask:<td>{cp}</td><td>{ville}</td>#}
{#set:this.ajaxIncludes=true#}
{#set:this.listContentUrl="villes.do"#}
{_ajx}
{_listContent}
	{_page}
		<div class="boxButtons">{_pageCounter}{_navBarre}</div>
{/_listContent}

Tester la page villes.do et son comportement (clavier et souris)

Action Effet
F2 Edition avec le formulaire de modification (il s'agit de la seule touche paramétrable)
Touches de direction
Haut, bas, gauche, droite
Déplacement entre les villes
MAJ+Home Atteindre la première page
MAJ+Fin Atteindre la dernière page
MAJ+PageUp Page précédente
MAJ+PageDown Page suivante
Double clic Déplacement sélection ligne

{#func:this.addSelector(113)#}
{#func:this.setEditable(false)#}
...

Activation de l'édition directe des membres

Pour qu'un membre soit éditable, il faut qu'il appartienne à un élément DOM de la classe editable.

Nous allons ajouter un Display associé à la classe KVille pour modifier l'affichage des éléments de la liste :
La méthode showInList à surdéfinir gère l'affichage de chaque membre de la liste.

package net.display;
import net.ko.displays.KObjectDisplay;
import net.ko.kobject.KObject;


public class VilleDisplay extends KObjectDisplay {

	@Override
	public String showInList(KObject ko, String memberName) {
		String result=super.showInList(ko, memberName);
		//si le nom du membre est cp ou ville, on le met dans un span de classe css editable
		if("cp".equals(memberName) || "ville".equals(memberName)){
			result="<span class='editable' title='"+memberName+"'>"+result+"</span>";
		}
		return result;
	}
}

...
	<class name="KVille" display="net.display.VilleDisplay">
		<member max="5" name="cp" required="1" type="string" />
		<member max="100" name="ville" required="1" type="string" transform="onlyFirstWordUpper"/>
	</class>
...

Tester l'édition possible de la liste :

Action Effet
Double clic Mode édition du membre
ESCAPE Sortie de l'édition
ENTREE Sortie avec possible validation
Perte du focus Sortie de l'édition

A noter que l'édition n'est pour l'instant que visuelle, et ne permet pas la validation des modifications effectuées.

Ajout de la validation

target correspond à l'élément DOM qui a reçu l'événement :

	<ajax-includes>
	...
		<request requestURL="villes.do">
			<js triggerSelector=".editable" triggerEvent="updated">
				<updateOne virtualURL="updateVille.do" operation="update" kobjectShortClassName="KVille" targetId="info" method="POST">
					<field name="{js:target.title}" value="{js:target.innerHTML}"/>
				</updateOne>
			</js>
		</request>
	...
	<ajax-includes>

	<mappings>
		...
		<virtualMapping requestURL="updateVille.do" mappingFor="updateOne"/>
		...
	</mappings>

tester l'édition finalisée dans la page villes.do

Message après modification et validation par ENTREE :

Ajout d'un champ supplémentaire dans la liste

Le champ supplémentaire devra afficher le nombre actuel d'entreprise(s) de la ville, dans un lien cliquable qui devra ensuite afficher la liste des entreprises de la ville.

...
	public KVille() {
		super();
		hasMany(KEntreprise.class);
	}
...

{#func:this.setEditable(false)#}
{#func:this.addSelector(113)#}
{#mask:<td>{cp}</td><td>{ville}</td><td>{btDetail}</td>#}
{#mask:<td>{cp}</td><td>{ville}</td><td>{btDetail}</td>#}
{#set:this.ajaxIncludes=true#}
{#set:this.listContentUrl="villes.do"#}
{_ajx}
{_listContent}
	{_page}
		<div class="boxButtons">{_pageCounter}{_navBarre}</div>
{/_listContent}

* Modifier le Display de la classe KVille, VilleDisplay, pour qu'il gère l'affichage de btDetail :

public class VilleDisplay extends KObjectDisplay {

	@Override
	public String showInList(KObject ko, String memberName) {
		String result=super.showInList(ko, memberName);

		if("cp".equals(memberName) || "ville".equals(memberName)){
			result="<span class='editable' title='"+memberName+"'>"+result+"</span>";
		}
		if("btDetail".equals(memberName)){
			KVille ville=(KVille) ko;
			int nb=ville.getEntreprises().count();
			if(nb>0)
				result="<div><a id='alink-"+ville.getId()+"' class='default' title='Entreprises de "+ville.getVille()+"'>"+KString.pluriel(ville.getEntreprises().count(),"entreprise")+"</a></div>";
			else
				result="<div>"+KString.pluriel(ville.getEntreprises().count(),"entreprise")+"</div>";
		}
		return result;
	}
}

Affichage des entreprises de la ville

{#func:this.setEditable(false)#}
{#func:this.addSelector(113)#}
{#mask:<td>{cp}</td><td>{ville}</td><td>{btDetail}</td>#}
{#mask:<td>{cp}</td><td>{ville}</td><td>{btDetail}</td>#}
{#set:this.ajaxIncludes=true#}
{#set:this.listContentUrl="villes.do"#}
{_ajx}
{_listContent}
	{_page}
		<div class="boxButtons">{_pageCounter}{_navBarre}</div>
{/_listContent}
<div id="divEntreprises"></div>

{#koDisplay:net.display.EntrepriseVilleDisplay#}
{#mask:<td>{rs}</td><td>{adresse}</td><td>{tel}</td>#}
{#mask:<td>{rs}</td><td>{adresse}</td><td>{tel}</td>#}
<fieldset>
<legend>%ville%</legend>
{_ajx}
{_listContent}
	{_page}
{/_listContent}
</fieldset>

Display

Le display va permettre de filtrer les entreprises de la liste, pour ne faire apparaître que les entreprises de la ville sélectionnée :
Surdéfinir la méthode beforeLoading de la façon suivante :

package net.display;

import javax.servlet.http.HttpServletRequest;

import net.ko.displays.KObjectDisplay;
import net.ko.http.objects.KRequest;
import net.ko.http.views.KPageList;
import net.ko.kobject.KObject;

public class EntrepriseVilleDisplay extends KObjectDisplay {

	@Override
	public void beforeLoading(Class<? extends KObject> clazz, KPageList list,
			HttpServletRequest request) {
		  list.addWhere("idVille='"+KRequest.GET("idVille", request, "-1")+"'");
	}

}

Mapping

	<mappings>
	...
		<mapping requestURL="entrepParVille.do" responseURL="/WEB-INF/list/ville/entreprise.list"/>
	...
	</mappings>

Inclusion ajax

	<ajax-includes>
	...
		<request requestURL="villes.do">
			...
			<js triggerSelector="a.default" triggerEvent="click">
				<include targetURL="entrepParVille.do" targetParams="idVille={js:$vId(target.id)},ville={js:target.title}" targetId="divEntreprises"></include>
			</js>
		</request>
	</ajax-includes>

  • Les paramètres passés (idVille et ville) à l'url entrepParVille.do le sont par l'intermédiaire de javascript : ils sont introduits par la balise {js:code javascript}
  • target correspond à l'élément cible de l'événement click : le lien cliqué
  • $vId permet de récupérer l'identifiant contenu dans l'id du lien (8 pour l'id "id-8" par exemple)

Tester le comportement de la vue villes.do (la touche F2 ou un clic sur le lien d'une ville sélectionnée provoque l'affichage des entreprises de la ville):

Juste pour le fun

	<ajax-includes>
	...
		<request requestURL="villes.do">
			...
			<js triggerSelector="a.default">
				<include targetURL="entrepParVille.do" targetParams="idVille={js:$vId(target.id)},ville={js:target.title}" targetId="divEntreprises" transition="skew">
					<transition targetId="divEntreprises">
						<oneTransition endValue="1" startValue="0" property="opacity" duration="2" timing="ease"/>
					</transition>
				</include>
			</js>
		</request>
	...
	</ajax-includes>

La suite : Gestion des entreprises >>