Publié par : yoannr | 30 mars 2010

Jstree , Json et IEnumerable & Extension Method…

Bonjour,

Je suis toujours à ma découverte de JQuery, et dernièrement je me suis frotté á la problématique du treeview avec Jquery. Il n’y a pas encore de tree officiel dans Jquery et j’ai opté pour un des arbres qui est présent sur la road map de JQuery, j’ai nommé jstree (http://www.jstree.com/).

Ce tree permet de faire tout ce dont j’ai envie, créer des nodes insérer, effectuer des postback, travailler sur du HTML, du Json. Bref il me semble vraiment complet et assez convivial dans son ergonomie.

La documentation est assez bien faite et elle m’a amené a vouloir développer mon arbre en prenant comme source du Json et en mode asynchrone. Pour ce faire rien de plus simple il y a un exemple dans la doc  que j’ai remanié pour avoir cela:

<script type=”text/javascript” class=”source”>
$(function()
{
$(“#MyTree”).tree(
{
data:
{
type: “json”,
async: true,
opts:
{
url: “/localisation/GetTree”
}
}
});
});
</script>
<div id=”MyTree”>
</div>

l’arbre “myTree”  prend en donnée du Json, de maniére asynchrone et sa source de donnée sera un appel À l’URL “/localisation/GetTree”. Localisation est evidemment le nom de mon controlleur et GetTree le nom de l’action associée:

public string GetTree(int? id)
{

Localisation localisation = CreateLocalisation();

//fetch all groups
localisation.Groups = servicelocalisation.GetLocalisationGroups();

string returnvalue = string.Empty;
returnvalue = localisation.Groups.ToJsTreeJson(g=>g.ID.Value, g=>g.Name, g=> g.ChildGroups);

return returnvalue;
}

Le service localisation va récupérer nos donnée et nous rendre une List<LocalisationGroup>  stockée dans localisation.Groups .

J’ai ensuite développé une extension méthode afin de transformer mon IEnumerable<T> en Json afin que mon jsTree puisse interpréter correctement ma liste de groupe.

Si on détaille cette extension méthode, on voit trois choses entrée en argument :

  • g=>g.ID.Value
  • g=>g.Name
  • g=> g.ChildGroups

Ces trois expression lambdas vont nous aider a retrouver nos informations dans un LocalisationGroup. Elles vont servir respectivement a retrouver l’identifiant de notre groupe, le nom de notre groupe, et les groupes enfant de notre groupe.

Passons de l’autre coté du miroir et regardons de plus prés cette extension method:

public static string ToJsTreeJson<TElement>(
this IEnumerable<TElement> elements,
Func<TElement, int> GetIDElement,
Func<TElement, string> GetDataElement,
Func<TElement, IEnumerable<TElement>> GetChildrenElements)
{
string returnvalue = string.Empty;

JsTree tree = new JsTree();
foreach (TElement element in elements)
{
tree.RootNodes.Add(CreateJstreeNode(element,GetIDElement, GetDataElement,GetChildrenElements));
}

returnvalue = tree.Serialize();

return returnvalue;
}

Les choses se compliquent? pas tant que ca…  C’est une extension méthode donc la liste de groupe que j’avais au départ est en fait passé en argument et nous allons la retrouver sous le nom éléments. Et que vois t’on? Que pour chaque élément de cette liste je vais ajouter un treenode à notre tree. Normal c’est ce que l’on veut faire. Et pour ce faire on utilise la méthode CreateJstreeNode.

Cette méthode prend en argument presque la même chose que notre extension methode:

private static  JsTreeNode CreateJstreeNode<TElement>(
TElement element ,
Func<TElement, int> GetIDElement,
Func<TElement, string> GetDataElement,
Func<TElement, IEnumerable<TElement>> GetChildrenElements)
{
JsTreeNode node = new JsTreeNode();

node.data = GetDataElement(element);
node.attributes = new jsAttribute();
node.attributes.id =  GetIDElement(element).ToString();
foreach (TElement e in GetChildrenElements(element))
{
node.children.Add(CreateJstreeNode(e,GetIDElement, GetDataElement, GetChildrenElements));
}

return node;
}

Sauf que au lieu de s’attacher à voir ce qui se passe dans une liste d’éléments, cette dernière ne s’intéresse qu’au noeud qu’on lui donne en argument. Et grâce aux fonctions données précédemment (g=>g.ID.Value, g=>g.Name, g=> g.ChildGroups), elle va être capable de comprendre comment décortiquer notre objet afin d’instancier un JsTreeNode .
Pour finir, cette méthode s’appelle récursivement afin de détailler tous les éléments enfants qui pourraient appartenir à un élément.

Pour chaque élément de notre liste cette méthode va être appelée et ensuite notre objet JsTree sera rempli. Il ne suffria plus alors qu’à lui demander de se sérialiser afin de produire le json correspondant.

Afin d’être exhaustif voici le modèle utilisé pour représenter mon jstree :

public class JsTree
{
delegate string GetData();
public IList<JsTreeNode> RootNodes { get; set; }

public JsTree()
{
RootNodes = new List<JsTreeNode>();
}

public string Serialize()
{
string returnvalue = “[";
foreach (JsTreeNode node in RootNodes)
{
returnvalue += JSONSerializer.Serialize<JsTreeNode>(node)+",";
}
if (returnvalue.Length > 1)
{
returnvalue = returnvalue.Substring(0, returnvalue.Length - 1);
}
returnvalue += "]“;

return returnvalue;
}
}

public class jsAttribute
{
public string id { get; set; }
}

public class JsTreeNode
{
private List<JsTreeNode> _children;

public string data { get; set; }
public jsAttribute attributes { get; set; }
public List<JsTreeNode> children
{
get
{
if (_children == null)
_children = new List<JsTreeNode>();
return _children;
}
set
{
_children = value;
}
}
}

et voilà pour le Json Serializer :

public class JSONSerializer
{
public string unescapedUrl { get; set; }

public static T Deserialise<T>(string json)
{
T obj = Activator.CreateInstance<T>();
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
obj = (T)serializer.ReadObject(ms);
return obj;
}
}

public static string Serialize<T>(T obj)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.Default.GetString(ms.ToArray());
}
}
}

Comme toujours, si vous avez des questions ou des commentaires sur ma manière de procéder , ne vous gêner pas, ce blog est un peu fait pour ca!

a++


Réponses

  1. Bonjour,

    Je trouve la doc très légère :( avec peux d’exemple
    (jqgrid par exemple a beaucoup d’exemple et cela aide énormément)

    Il n’y a pas d’exemple pour gérer le clic ou dblclick sur un node pour par exemple ouvrir un lien + passage des paramêtres….

    Si vous avez une idée de comment faire cela ?

    Merci d’avance

    • Pour la doc, il me semble qu’ils ont tout rechanger depuis car je me rappelle avoir eut beaucoup plus d’exemples que cela.
      Je viens de retelecharger cette vieille doc v 0.99 et je ne sais pas si elle est compatible avec la nouvelle version du composant mais elle possède un répertoire demo avec des exemples de code comme celui ci qui peut donner des idèes pour ouvrir un lien + passage de parametres (Basic JSON demo):
      $(function () {
      $(“#basic_json_1″).tree({
      data : {
      type : “json”,
      opts : {
      static : [
      {
      // the short format demo
      data : "A node",

      // here are the children
      children : [
      { data : "Child node 1" },
      { data : "Child node 2" },
      { data : "Child node 3" }
      ]
      },
      {
      attributes : { “id” : “li.node.id” },

      // this is the long data format
      data : {
      title : “Long format demo”,
      attributes : { “href” : “http://jstree.com” }

      }
      }
      ]
      }
      }
      });
      });


Répondre

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Twitter picture

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Connexion à %s

Catégories

Suivre

Get every new post delivered to your Inbox.