Aide - Recherche - Membres - Calendrier
Version complète : Utiliser le composant Tree
Centre de Formation Flash - Forums Adobe Flash > Rich media et intégration > Flex
Tonic
Bonjour à tous,

j'avais développé il y a quelques mois une fonction en AS2 permettant de trouver un élément dans un composant Tree (ouverture des noeuds jusqu'à cet éléments, et sélection automatique de cet élément).

La voici :

Actionscript
 
/**
* recursiveFunctionSearchElement
* @param node : node en cours
* @param searchElement : chaine à rechercher
*/

private function recursiveFunctionSearchElement(node : XMLNode,searchElement : String):Void
{
var childs:Array = node.childNodes;
var l:Number = childs.length;
var pos : Number =0;
// ouverture de la node en cours
Tree_tr.setIsOpen(node, true);
//findTagDefault est un booleen déclaré dans ma classe
while((!findTagDefault)&&(pos<l))
{
if(childs[pos].hasChildNodes())
{
// rappel de la fct sur les enfants
recursiveFunctionSearchElement(childs[pos],searchElement);
}
else
{
if (childs[pos].attributes["data"] == searchElement)
{
//selection de la node et positionnement de la scroll
Tree_tr.selectedNode = node.childNodes[pos];
Tree_tr.vPosition = Tree_tr.getDisplayIndex(Tree_tr.selectedItem);
findTagDefault = true;
}
}
pos++;
if((!findTagDefault)&&(pos==l))
{
// fermeture de la node en cours si l'element n'a pas été trouvé
Tree_tr.setIsOpen(node,false);
}
}
}
 


J'essai de transposer ce code en AS3, mais c'est plutôt laborieux... icon_sad.gif
Je n'arrive pas à trouver les fonctions correspondante à celles-ci :

- node.childNodes
- setIsOpen(node, true);

Donc concrètement, je n'arrive pas à parcourir le contenu de ma Tree, c'est à dire à récupérer les noeuds en cours d'exploration (j'ai essayé montree.dataProvider.getItemAt(...), mais c'est pas terrible).
Et impossible d'ouvrir et de fermer une tree par code...

Voilà, si quelqu'un pouvais y jetter un coup d'oeil...

Merci d'avance icon_smile.gif
liguorien
salut,

l'équivalent de node.childNodes, c'est node.elements() qui retourne une XMLList.

l'équivalent de tree.setIsOpen(node, true), c'est tree.expandItem(node, true);

@++
Tonic
Salut,

merci liguorien pour ces infos gourou.gif

Bon, j'ai avancé un peu, mais c'est très laborieux, je dois dire que je m'arrache les cheveux avec tout les nouveaux objets dérivants du XML. (et ca n'a plus grand chose à voir avec l'as2)

Bon voici mon code :

Actionscript
 
/**
* recursiveFunctionSearchElement
* @param node : node en cours
* @param searchElement : chaine à rechercher
*/

public function recursiveFunctionSearchElement(node :XML,searchElement : String):void
{
var childs: XMLList = node.elements();
var l:Number = childs.length();
var pos : Number =0;
//ma Tree
listTagTree.expandItem(node, true);

while((!findTagDefault)&&(pos<l))
{
var leXML : XML = childs[pos];
//c'est ici que ca coince, impossible de trouver une fonction renvoyant une xmlNode
var laNode : XMLNode = leXML...;

if(laNode.hasChildNodes())
{
// rappel de la fct sur les enfants
recursiveFunctionSearchElement(laNode,searchElement);
}
else
{
if (childs[pos].attributes["data"] == searchElement){
//selection de la node et positionnement de la scroll
//listTagTree.selectedNode = node.childNodes[pos];
//listTagTree.vPosition = Tree_tr.getDisplayIndex(listTagTree.selectedItem);
//trace("trouve"); //findTagDefault = true;
}
}
pos++;

if((!findTagDefault)&&(pos==l))
{
// fermeture de la node en cours si l'element n'a pas été trouvé
listTagTree.expandItem(node,false);
}
}
}
 


Mon problème, c'est que je n'arrive pas à récupérer une node de mon XML (XMLNode), et donc de pouvoir appliquer la fonction "hasChildNode" pour savoir si je dois continuer la récurivité.
Tout ce que j'arrive à faire, c'est de créer de nouveaux XML ou XMLList, ce qui ne convient pas pour ce que je veux faire.

Merci d'avance pour vos conseils... icon_smile.gif
iteratif
La classe XML possède une méthode children() qui te retourne une XMLList des enfants contenu dans ce dernier, il te suffit en suite sur la XMLList de tester la fonction length() pour savoir si tu as des enfants, ex :

Actionscript
 
var node:XML = childs[pos];
var childrens:XMLList = node.children();
 
if(childrens.length()) {
...
}
 
Tonic
Merci iteratif gourou.gif

J'arrive désormais à trouver un élément dans mon arbre, le problème c'est que je n'arrive pas à ouvrir les noeuds de l'arbre menant à cette élément.

Enfin, j'arrive à ouvrir le 1er noeud de l'arbre avec :
Actionscript
 
listTagTree.expandItem(listTagTree.dataProvider.getItemAt(0), true);
 


listTagTree.dataProvider.getItemAt(0) me ramène l'ensemble de mon arbre, donc logique qu'il ouvre le 1er noeud.

Par contre, impossible d'ouvrir les noeuds enfants, c'est à dire :
Actionscript
 
listTagTree.expandItem(childs[pos], true);
 

childs[pos] étant un des sous noeuds du noeud principale.
Donc j'ai l'impression, qu'il est impossible d'ouvrir un noeud de l'arbre sans mettre en parametre de la méthode expandItem le dataProvider, et nom un de ses neouds.

Y a t'il une solution? un exemple?

Merci d'avance... icon_smile.gif

PS : je retourne m'arracher les cheveux sur ce Tree... icon_sad.gif

PS2 : juste un test pour vous préciser ce qui ne marche pas :

Ca marche :
Actionscript
 
listTagTree.expandItem(listTagTree.dataProvider.getItemAt(0), true);
 


Ca marche pas :
Actionscript
 
var xml : XMLList = new XMLList(listTagTree.dataProvider.getItemAt(0));
listTagTree.expandItem(xml, true);
 
iteratif
Tu as aussi expandChildrenOf () qui te permet d'ouvrir tout ce qui se trouve a partir du noeud que tu passes en parametre.
Tonic
Ben mon problème reste identique :
Ca marche :
Actionscript
 
listTagTree.expandChildrenOf(listTagTree.dataProvider.getItemAt(0), true);
 


Ca marche pas:
Actionscript
 
var xml : XMLList = new XMLList (listTagTree.dataProvider.getItemAt(0));
listTagTree.expandChildrenOf(xml, true);
 


Donc concrètement moi ca donne ca :
Actionscript
 
// j'ouvre le premier noeud et je lance la fonction de recherche
listTagTree.expandItem(listTagTree.dataProvider.getItemAt(0), true);
var xmlList : XMLList = new XMLList(listTagTree.dataProvider.toString());
 
recursiveFunctionSearchElement(xmlList,"tag008");
 
/**
* recursiveFunctionSearchElement
* @param node : node en cours
* @param searchElement : chaine à rechercher
*/

public function recursiveFunctionSearchElement(node :XMLList,searchElement : String):void
{
var childs: XMLList = node.elements();
var l:Number = childs.length();
var pos : Number =0;
var pos2 : Number = 0;

while((!findTagDefault)&&(pos<l))
{
var childrens:XMLList = childs[pos].children();
//récupère un des noeuds de l'arbre, mais impossible de l'ouvrir !
listTagTree.expandItem(childs[pos], true);

if(childrens.length()>0)
{
// rappel de la fct sur les enfants
while(pos2<childrens.length() && !findTagDefault)
{
if(childrens[pos2].attribute("data")== searchElement) {
findTagDefault = true;
}
pos2++;
}
if(!findTagDefault)
{
recursiveFunctionSearchElement(childrens,searchElement);
}else
{
//listTagTree.expandItem(childrens, false);
}
}
else
{
if (childs[pos].attributes.@data == searchElement)
{
findTagDefault = true;
}
}
pos++;

if((!findTagDefault)&&(pos==l))
{
// fermeture de la node en cours si l'element n'a pas été trouvé
//listTagTree.expandItem(node,false);
}
}
}
 


Ce que je crains, c'est qu'il soit impossible d'ouvrir un noeud d'une Tree avec "expandItem" ou "expandChildrenOf" en passant autre chose que le dataProvider en paramètre (soit donc un des noeuds du dataProvider mit dans une variable).
Le problème à ce moment là, serait de savoir comment récupérer un noeud spécifique dans mon dataProvider sans passer par une variable intermédiaire...

Si quelqu'un à des idées, d'autres pistes... gourou.gif
liguorien
salut,

qu'est-ce que ta méthode doit faire exactement ?

rechercher récursivement dans le document XML un élément dont la valeur de l'attribute "data" est égale à la variable "searchElement" et ouvrir le noeud de l'élément trouvé ?


si la réponse est oui, alors ceci devrait faire l'affaire :

Actionscript
public function doSearch(tree:Tree, node:XML, search:String):void{

for each(var result:XML in node..*.(@data == search)){

var liste:Array = [result];
var temp:XML = result;

while((temp = temp.parent()) != null){
liste.unshift(temp);
}
 
for each(var item:XML in liste){
tree.expandItem(item, true);
}
}
}


@++
Tonic
Merci liguorien, gourou.gif mais ca ne marche pas. icon_sad.gif

En effet, le script tourne indéfiniment en boucle, et ne parvient pas non plus à ouvrir de node. De plus, ce qu'il me faut, c'est une fonction récursive, car je ne connais pas à l'avance la structure de mon arbre.

De mon côté, j'ai avancé, mais je n'aie toujours pas trouvé LA solution (malgré qu'il me reste très peu de cheveux sur la tête=>pétage de plomb sur ce script).
J'arrive à trouver l'élément cherché, mais impossible d'ouvrir les nodes...

J'ai me suis quand même apêrçu de quelque chose (merci à arnaud qui ma mit sur la piste icon_wink.gif ):
Actionscript
 
// ca marche
listTagTree.expandChildrenOf(listTagTree.dataProvider.getItemAt(0), true);
 
// ca ne marche pas
var xml : XMLList = new XMLList (listTagTree.dataProvider.getItemAt(0));listTagTree.expandChildrenOf(xml, true);
 
// ca marche
var obj : Object = new Object();
obj = listTagTree.dataProvider.getItemAt(0);
listTagTree.expandItem(obj , true);
 


Donc en gros, il faut typer le paramètre en "Object", sinon, impossible d'ouvrir une node.

Mais impossible de mettre ceci en pratique dans ma fonction recursive...
Si quelqu'un pouvait y jeter un coup d'oeil.
Voici ma fonction (pas très optimisée, et peut être incorrect au final, mais je n'aie plus assez de recul... icon_sad.gif )

Actionscript
 
/*
* recursiveFunctionSearchElement
* @param node : node en cours
* @param searchElement : chaine à rechercher
*/

public function recursiveFunctionSearchElement(objParam :Object,searchElement : String):void
{
trace("==============================");
trace(objParam)
trace("==============================");

// Cette fonction marche pour la toute première récursivité
listTagTree.expandItem(objParam, true);
//typage en XML du paramètre
var xmlBase : XML = new XML(objParam);

var posParent : Number =0;
var posEnfant : Number;
var sizeAttribute :Number;
var objetEnfant : Object;
var objetList : Object;
if(xmlBase.hasComplexContent())
{
var xmlList : XMLList = xmlBase.children();

objetList=new Object();
objetList=objParam.children();
listTagTree.expandItem(objetList, true);

while((posParent<xmlList.length())&&(!this.findTagDefault))
{
var xmlEnfant : XML = new XML(xmlList[posParent]);
objetEnfant = xmlEnfant;

if(!xmlEnfant.hasComplexContent()){
var listAttribute:XMLList=xmlList.attribute("data");
sizeAttribute=listAttribute.length();
posEnfant= 0;

while((posEnfant<sizeAttribute)&&(!this.findTagDefault)){
if(listAttribute[posEnfant]==searchElement)
{
this.findTagDefault=true;
/*listTagTree.selectedItem=childrens[posEnfants];
listTagTree.verticalScrollPosition=listTagTree.selectedIndex;
*/

trace("trouve");
}
posEnfant++;
if((posEnfant==sizeAttribute)&&(!this.findTagDefault))
{
//listTagTree.expandItem(objetEnfant, false);
}
}

}else
{
if(!this.findTagDefault)
{
recursiveFunctionSearchElement(objetEnfant,searchElement);
}
}
posParent++;

}
//if(!this.findTagDefault)listTagTree.expandItem(objetList, false);
}
}
 
//1er appel
var obj : Object = new Object();
obj = listTagTree.dataProvider.getItemAt(0);
recursiveFunctionSearchElement(obj,"tag060");
 


Merci d'avance...
liguorien
CITATION(Tonic @ Nov 14 2006, 05:33 PM) *

Merci liguorien, gourou.gif mais ca ne marche pas. icon_sad.gif

En effet, le script tourne indéfiniment en boucle, et ne parvient pas non plus à ouvrir de node. De plus, ce qu'il me faut, c'est une fonction récursive, car je ne connais pas à l'avance la structure de mon arbre.

Hum, c'est bizarre car lorsque j'ai testé mon script ça fonctionnait très bien. Et la recherche dans le XML est récursive (opérateur ..)

Peux-tu envoyer un échantillon de ton XML et de la chaine de recherche ?
iteratif
Trés bizarre pour moi ca marche nickel icon_neutral.gif
Tonic
Merci à vous de jeter un oeil gourou.gif

Mon fichier xml est généré depuis un objet renvoyé par un service, mais voici l'arbre généré.
Tonic
Re,

je me suis penché sur la fonction de liguorien, et en fait, ce n'est pas elle qui tournais en boucle, mais un script que j'avais un peu plus haut icon_redface.gif (quel couillon).

En fait, vu que ma Tree se recharche régulièrement, avec une recherche différente à faire, j'utilise :
Actionscript
 
listTagTree.addEventListener(FlexEvent.UPDATE_COMPLETE,searchElement);
 

Puisque si je lance ma fonction de recherche tout de suite après avoir rempli mon dataProvider, j'ai un joli message d'erreur...

Donc la fonction searchElement lance la fonction de liguorien une fois l'évènement reçu...
Mais en fait, c'est évènement est appelé x fois lorsque je rerempli ma Tree. Ce n'est donc surement pas l'évènement correct que j'utilise. Bref, pour le moment j'ai coutourner le problème en mettant un boolean pour ne lancer qu'une seule fois la fonction...

La fonction trouve éffectivement les bon noeuds à ouvrir (j'ai tracé tout cela pour m'en rendre compte).
Mais j'en revient au problème que j'avais cité, il n'ouvre tout de même pas les nodes.
J'ai transformé la variable xml en object, mais ca ne change rien :

Actionscript
 
public function doSearch(tree:Tree, node:XML, search:String):void
{
for each(var result:XML in node..*.(@data == search))
{
var liste:Array = [result];
var temp:XML = result;
while((temp = temp.parent()) != null)
{
liste.unshift(temp);
}
for each(var item:XML in liste)
{
var obj : Object = item;
// il m'affiche bien les noeuds à ouvrir mais rien ne se passe
trace("item :"+item);
tree.expandItem(obj, true);
}
}
}
 


Donc voilà, c'est peut être mon xml qui est mal formé, mais à 1ère vu non, puisque j'ai testé le fichier que j'ai joint dans le post précedent dans un navigateur, et il ne semble pas poser problème.

Merci d'avance pour vos conseils gourou.gif
Tonic
Bon, après des heures de débuggage, de modification, j'ai enfin trouvé ce qui n'allait pas.

Et je dois dire que je suis un peu dégouté d'avoir passé autant de temps pour un problème comme celui-ci. icon_sad.gif

* 1er problème : tout se passe lors de l'appel de la fonction.

Comme je le faisais depuis le debut :
Actionscript
 
//ne marche pas
doSearch(listTagTree,new XML(listTagTree.dataProvider.getItemAt(0)),"tag060");
 

Et maintenant :
Actionscript
 
//marche
doSearch(listTagTree,listTagTree.dataProvider.getItemAt(0),"eltRecherche");
 


Concrètement, je créé un "XML" à partir d'un "Object", ce qui paraissait logique vu le typage de l'élément en entré.
Sauf que non, l'objet renvoyé par le dataProvider suffisait.
Disons que j'ai trouvé pourquoi ca ne marchait pas, mais je ne comprend pas pourquoi la 1ère version ne marchait pas icon_eek.gif .

* 2ème problème :

Quand on trace les items, on peut se rendre compte que le dernier item est vide. Donc concrètement, impossible de faire la selection de l'élément trouvé en toute logique. Sauf que non, quand on trace il ne s'affiche rien, mais quand on balance à la propriété selectedItem cette élément vide, ca marche.

Donc voilà, ca marche, mais je ne comprend pas tout.
Alors bug, incohérence, mauvaise utilisation, je ne sais pas... icon_rolleyes.gif

Voici la version finale, permettant donc d'ouvrir les node jusqu'à l'élément, selectionner l'élement cherché, et placer la vScrollPane au bon endroit...

Actionscript
 
public function doSearch(tree:Tree, node:XML, search:String):void
{
var position : Number;
var findAttribute : Boolean = false;
for each(var result:XML in node..*.(@data == search))
{
var liste:Array = [result];
var temp:XML = result;
while((temp = temp.parent()) != null)
{
liste.unshift(temp);
}
for each(var item:XML in liste)
{
trace(item);
tree.expandItem(item, true);
}
tree.selectedItem=liste[liste.length-1];
tree.verticalScrollPosition=listTagTree.selectedIndex;

}
}
 
//lancement de la recherche
doSearch(listTagTree,listTagTree.dataProvider.getItemAt(0),"eltRecherche");
 


Voilà, j'espère que ca pourra servir à quelqu'un, et je termine mon petit monologue icon_mrgreen.gif en remerciant Liguorien et Iteratif... gourou.gif
klikissy
Salut les gars, vous avez l'air de toucher... Perso je suis sur le Tree et je vais pas tarder a taper ma tete contre l'ecran.

Je crée dynamique un tree (sans probleme) via un xml.
Le truc c'est que les enfants de ce tree son en fait des criteres d'affinage, je peu en selectionner plusieurs et les deselectionner.
A chaque selection je "send" une info id des enfants selectionnés.
Au clic je recupere correctement l'id de chaque enfants.

Pour le reste je suis largué.

SVP un ptit coup de main je vous lache mes sources si vous voulez... gourou.gif gourou.gif gourou.gif
Tonic
CITATION(klikissy @ Apr 6 2007, 02:49 PM) *

Salut les gars, vous avez l'air de toucher... Perso je suis sur le Tree et je vais pas tarder a taper ma tete contre l'ecran.

Je crée dynamique un tree (sans probleme) via un xml.
Le truc c'est que les enfants de ce tree son en fait des criteres d'affinage, je peu en selectionner plusieurs et les deselectionner.
A chaque selection je "send" une info id des enfants selectionnés.
Au clic je recupere correctement l'id de chaque enfants.

Pour le reste je suis largué.

SVP un ptit coup de main je vous lache mes sources si vous voulez... gourou.gif gourou.gif gourou.gif


Salut,

quelle est ta question précisement? icon_rolleyes.gif
nospam94
Merci pour les informations qui m'ont été très precieuses. Le composant Tree est effectivement assez casse tête, que de temps perdu parfois !!!

Et au passage vu que c'est mon premier post, bonjour à vous tous.
Ceci est une version "bas débit" de notre forum. Pour voir la version complète avec plus d'informations, la mise en page et les images, veuillez cliquer ici.
Invision Power Board © 2001-2008 Invision Power Services, Inc.