Utiliser Knockout JS et Sharepoint 2010 avec ListData.svc – Exemple de gestion de favoris

Utiliser Knockout JS et Sharepoint 2010 avec ListData.svc – Exemple de gestion de favoris


Le but de ce post est vous montrer un exemple d’utilisation du Framework JavaScript Knockout JS et ses possibilités avec notamment SharePoint 2010. Pour illustrer tout cela, je vais vous montrer comment implémenter un système de gestion de base de favoris pour un utilisateur d’un site SharePoint avec ajout/suppression de données via Listdata.svc.

Voici le résultat final de cette solution:

Résultat final

Un mot sur Knockout JS

Pour ceux qui ne le savent pas, le Framework Knockout JS vous permet d’implémenter le design pattern MVVM (Modèle-Vue-Vue Modèle) directement en JavaScript. Cela peut s’avérer très pratique pour d’une part mieux structurer votre code « coté client », mais aussi réduire la quantité de code nécessaire pour réaliser des applications dynamiques, grâce à la notion de « bindings déclaratifs ».
Le but de ce post n’étant pas de vous présenter le Framework en tant que tel, je vous conseille, fortement, pour en apprendre davantage, d’aller jeter un œil sur le site officiel qui regorge d’exemples pratiques particulièrement explicites  http://knockoutjs.com/.

 Note: Les versions des librairies utilisées dans l’exemple sont les suivantes:

  • Knockout JS 2.1.0 pour l’implémentation de la logique du sysème
  • JQuery 1.8.1 pour les manipulations d’objets en JavaScript en également les appels ajax

Présentation du système

Revenons à notre système de gestion de favoris. Celui-ci, très simple dans le cadre de notre exemple, se présente de la manière suivante:

Cas d'utilisation de l'exemple

Pour pouvoir gérer la notion de favoris et de groupes, nous définissons le modèle de données relationnel suivant:

Modélisation du cas d'exemple

Un groupe peut contenir 0 ou plusieurs favoris et un favori n’est contenu que dans un seul groupe à la fois. Voici les attributs de base pour chacune des entités:

Group

Favorite

  • Id : Identifiant unique du groupe
  • Name : Nom du groupe
  • Owner: Utilisateur propriétaire du groupe
  • Id : Identifiant unique du favori
  • Title : Titre du favori
  • Owner: Utilisateur propriétaire du favori
  • Value : Valeur du favori. Dans notre exemple, on suppose que celle-ci est une url mais elle pourrait très bien être autre chose.

Création de la structure de données dans SharePoint

Pour chaque entié, nous créons un type de contenu particulier avec les champs de notre modèle de données. Pour éviter toute étape de création manuelle dans SharePoint , nous créons également une définition de liste ainsi qu’une instance de liste pour chacune des entités. Je ne détaille pas ces étapes ici car c’est n’est pas l’objectif de ce post.

Au final, nous aurons une liste « Favorites » et une liste « Groups ».

Relation entre les listes

Pour respecter la multiplicité définie dans le modèle de données dans SharePoint, il nous faut définir un champ de type « Lookup » dans la liste « Favoris » pointant sur la liste « Groups ».

Cependant, pour gérer la suppression en cascade, c’est à dire que lorsqu’un groupe est supprimé dans la liste , tous ses favoris le sont également, il nous faut configurer la colonne « GroupName ». Pour cela, la configuration se fait dans un feature receiver, à l’activation de celle-ci:

Cette action peut aussi être effectuée via l’interface de SharePoint, dans les propriétés de la colonne directement dans la liste:

Configuration de la supression en cascade

Implémentation du modèle KnockoutJS

Une fois notre structure de données définie, il nous reste à implémenter le modèle complet KnockoutJS qui sera en charge de toute la logique du système. Dans mon exemple, j’utilise un composant Visual WebPart (notamment pour gérer la vue). De même les fichiers JavaScript sont séparés selon leur utilité (entité, modèle, vue-modèle) et tous provisionnés dans la bibliothèque « Style Library » du site racine de la collection de sites.

Entités

Les entités de notre modèle de données sont les objets « métier » de notre système. Il sont traduits en objets JavaScript. Chacun des champs correspond à celui du type de contenu correspondant dans SharePoint:

« Favorite »

« Group »

Pour pouvoir gérer les dépendances d’ojets observables dans Knockout JS, à savoir qu’un favori est obligatoirement contenu dans un groupe et qu’un groupe ou un favori peuvent être créés idépendamment dans notre modèle, nous ajoutons tout d’abord un tableau observable « UserFavorites » à l’objet « Group » puis une méthode permettant d’ajouter un élément dans ce même tableau. Pour finir, la méthode bind(this) permet de lier la méthode d’ajout de favori et par conséquent le tableau observable « UserFavorites » à l’objet « Group« , qui, nous le verrons plus tard est aussi un tableau observable. Par ce moyen, nous créons ainsi une dépendance entre les deux tableaux observables, ce qui signifie que lorsqu’un favori sera ajouté dans au tableau « UserFavorites« , l’objet « Group » sera également mis à jour dans le tableau observable qui le contient.

Pour rappel, Knockout JS ne tient absolument pas compte du type d’objet présent dans un tableau observable (ce qui fait que je peux passer des objets de type « Jambon » à la place de type « Favori » sans que cela ne gêne outre mesure ;)).

Modèle

Le modèle est la « classe » permettant l’accès au données et typiquement la gestion des opérations CRUD (Create, Read, Update, Delete). Dans notre cas, les données sont contenus dans des listes SharePoint. Comme Knockout JS utilise du JavaScript coté client, nous utiliserons le service web Listata.svc fournit avec SharePoint 2010, qui répond parfaitement à ce besoin.Pour plus d’informations sur ce service web, ça se passe ici: http://msdn.microsoft.com/en-us/library/ff798339.aspx.

Lecture des données

La fonction qui permettant de lire des données (peu importe le type) via le service web Listdata.svc est la suivante:

Plusieurs choses à noter ici:

  • Tout d’abord, nous passons en paramètre une fonction JavaScript. Cette fonction est en réalité  celle implémentée dans la classe Vue-Modèle, qui se chagera d’ajouter les éléments dans le tableau observable correspondant, en parcourant les objets JSON (d.results) retournés par l’appel ajax.
  • Deuxièmement, nous passons une requête sous forme d’url. Cette requête est en réalité celle passée au web service Listdata.svc pour récupérer les données. Par exemple pour récupérer la liste de tous les favoris d’un groupe la requête correspondante sera celle-ci:
Encore une fois, je vous conseille de regarder la documentation sur ce service qui détaille comment faire des « join » dans les requêtes avec Listdata.svc via des champs de types « Lookup« .
De même, je vous invite à taper cette url directement dans votre navigateur et observer la réponse JSON obtenue pour mieux comprendre le fonctionnement.
Vous remarquerez peut être que les paramètres de la requête sont des identifiants et non un nom d’utilisateur ou un nom de groupe. En observant la réponse JSON renvoyée par Listdata.svc, on s’aperçoit que SharePoint gère les champs de types « Lookup » et « Person » de la manière suivante:

  • Il créer un nouveau nom de colonne fictif en concaténant le nom de la colonne original avec « Id« , contenant l’id de référence de l’élément réel dans la liste source. C’est pour cela que la colonne « GroupName » devient « GroupNameId » dans la réponse. Ce point a son importance, car lors des opérations d’ajout, de modification ou de supression, c’est bien ce nom de colonne que vous devrez utiliser et non celui par défaut!

 Ajout d’un nouvel élément

L’ajout d’un élément se fait de la manière suivante:

Ici aussi plusieurs choses à noter:

Dans le cas d’un ajout, l’url de requête correspond tout simplement au nom de la liste:

Le web service se base sur le display name des colonnes SharePoint, ce qui fait que vous devez obligatoirement traduire le nom des champs de votre objet à insérer dans la langue souhaitée avant d’effectuer l’appel.

Listdata.svc va en réalité convertir les propriétés de votre objet d’entrée en nom de colonne SharePoint. Ainsi si vous voulez insérer un objet avec le champ « Titre » alors que la langue courante est Français, cela ne fonctionnera pas :(, il faut que le champ se nomme « Title ». Très pratique me direz-vous quand vous avez plusieurs langues à gérer…

Pour rappel, vous devez utiliser le nom de colonne original + »Id » pour insérer un élément dans un champ de type « Lookup » ou « Person ».

La méthode $ajax()vous permet de récupérer des informations sur l’élément nouvellement inséré et notamment l’id. Pour cela, le paramètre dataTypede la méthode doit être positonnée à « html » au lieu de « json » (bien que la réponse soit du JSON) car sinon, des navigateurs comme Chrome ou Firefox vous renverons un objet différent de IE par exemple, que vous ne pourrez pas parser correctement avec JQuery. Il semblerait que « html » fonctionne correctement sur Safari, Chrome, IE et Firefox. De même, l’appel doit se faire de manière synchrone et non asynchrone pour être certain de récupérer l’id de l’élément inséré (paramètre async: false).

Suppression d’un élément

La suppression d’un élément est effectuée de la manière suivante:

Ici rien de spécifique, la requête de suppresion est la même que celle d’ajout à ceci près que l’identifant de l’élément dans la liste SharePoint est passé en paramètre.

Vue-Modèle

La classe Vue-mMdèle ou View Model en anglais permet, comme son nom l’indique, de faire la liaison entre la partie présentation et accès aux données de l’application. Dans notre cas, c’est elle qui se charge de définir les éléments observables et leur gestion.

Définition des variables

La définition des variables dans le View-Model correspond principlament à la définition des éléments observables de l’applications, qui vont pouvoir être « bindés » à la vue.

Voici les éléments que nous définissons dans cette classe:

Vous remarquerez que je ne défini pas ici d’élement observable pour les favoris. En effet,ce sont mes entités « Group » qui gèrent cela comme vu un peu plus haut.

L’initialisation du ViewModel est faite avec l’identifiant de l’utilisateur SharePoint courant.

Récupérer la hiérarchie de favoris

Voici la méthode qui permet de récupérer toute la hiérarchie des favoris de l’utilisateur:

Les méthodes « GetAllGroups » et « GetAllFavorites » appartienent à la classe de modèle et se chargent principalement de construire l’url de requête pour le service web Listdata.svc, avant d’appeler un méthode générique de lecture d’éléments. Ces méthodes parcourent les résultats JSON renvoyés par cette dernière et créer un nouvel objet du type souhaité.

Par ailleurs, vous noterez également ici que nous sommes (encore?) obligés de convertir le nom des champs renvoyés pour qu’ils correspondent à la langue courante, la service web se basant également sur le display name des colonnes en mode lecture :).

Pour chaque résultat, celui-ci est ajouté dans son tableau observable correspondant

  • UserFavorites, via la méthode addFavorite de l’entité « Group« , pour un favori.
  • UserGroupspour un groupe de favoris.

Ajouts

L’ajout d’un élément, que cela soit un groupe ou un favori, est gérée via un event handler que l’on « bind » à une classe CSS  « add » pour permettre un traitement générique. Ce « binding » est effecuté grâce à JQuery de la manière suivante:

Vous voyez ici la définition des objets à insérer. Dans cet exemple, j’utilise toujours le même objet ce qui n’a pas vraiement d’intérêt. A vous de créer vos objets de la manière dont vous souhaitez en gardant à l’esprit que les noms de champs doivent correspondrent ceux de la liste SharePoint.

Ajouter un groupe de favoris

L’ajout d’un groupe se fait de la manière suivante:

On insère l’objet dans SharePoint, récupère son identifiant, et l’insèrons dans le tableau observable.

Ajout d’un favori unique

Ici, même chose que pour un groupe, à la différence que l’on ne manipule pas directement le tableau observable mais la méthode qui permet de l’ajouter dans l’entité « Group ».

Supressions

La suppression d’un élément, que cela soit un groupe ou un favori, est gérée via un event handler que l’on « bind » à une classe CSS  « remove » pour permettre un traitement générique. Ce « binding » est effecutée grâce à JQuery de la manière suivante:

Grâce à la méthode ko.contextFor(this), nous sommes capables de récupérer le contexte courant Knockout JS de l’élément DOM. Ainsi, nous pouvons identifier si l’on se trouve sur un groupe ou un favori uniquement grâce au contexte.

  • La variable $root correspondant au contexte du ViewModel d’où l’on peut appeler ses méthodes.
  • $data correspond à l’élement courant bindé (un objet groupe, ou favori).
  • $parent correspond au contexte parent de l’élément DOM courant (typiquement le « data-bind » d’un foreach dans la vue).

Une fois le contexte identifié, on supprime l’élment dans son tableau observable correspondant en l’ayant au préalable supprimé dans SharePoint.

Supprimer un groupe de favoris

La suppression d’un groupe dans SharePoint est effecutée de la manière suivante:

L’objet « Group » est supprimé ainsi que tous ses favoris dans SharePointgrâce à la méthode RemoveGroup().

Supprimer un favori

La suppression d’un favori est identique à celle d’un groupe:

Le favori est supprimé uniquement de la liste SharePoint.

Vue

Dernier élément de notre système, la vue permet l’affichage des données grâce à différents « bindings » sur les tableaux observables.

Voici la vue correspondant à notre ViewModel:

L’affichage se fait grâce à deux « foreach » imbriqués gérants la relation de dépendance entre un groupe et ses favoris. Vous noterez l’utilisation du « data-bind=’with: $data.xxx » permettant d’identifer explicitement le contexte en dehors de ces deux boucles sur les classes CSS « add » dans l’event handler associé.

Résultat final

Voici le résultat final de notre sytème:

Résultat final

 

A partir de là vous pouvez:

  • Voir tous les favoris
  • Ajouter un groupe
  • Ajouter un favori dans un groupe
  • Supprimer un groupe et tous ses favoris
  • Supprimer un favori

Je vous laisse le soin d’apprécier le dynamisme qu’apporte Knockout JS ;).

Conclusion

Knockout JS permet de faire des applications riches et dynamiques avec un minimum de code. Couplé à SharePoint 2010, il peut s’avérer très utile pour de l’affichage de données de listes comme illustré dans cet exemple.
De plus, étant complétement coté client, vous pourrez très certainement utiliser la majorité de ce code pour SharePoint 2013! N’ésitez pas à commenter et à améliorer ce code!

+ There are no comments

Add yours