ID, Name, ClientID et UniqueID c’est quoi au juste ?
Par amethyste le Nov 16, 2007 | Dans focus | Réagir »
D’abord je souhaiterai juste signaler un article de Jean-Marie Thia sur les Web SSO [1]. C’est un truc que je ne connaissais pas et qui entre dans la lignée de la série d’articles que j’ai publié cet été sur l’authentification. L’article est très complet avec une solution clef en main.
L’article qui suit est particulier car il était plutôt destiné au blog que j’anime sur l’intranet. J’ai entamé une longue série sur l’écriture des composants ASP et j’en suis venu naturellement à parler de l’identification d’un contrôle. Mais il me semble que sous des apparences inoffensives, cette question soulève en fait diverses difficultés qui sont au cœur même du fonctionnement d’ASP.NET et de toutes les technologies de pages Web interactives. Ces difficultés sont en général mal comprises et peu documentées.
J’ai donc décidé de le publier également sur mon blog Améthyste. Le prochain article sera la suite d’un des articles de l’intranet, mais uniquement publié ici !
Une des propriétés les plus importantes d’un composant est son ID. L’ID est la valeur unique qui va permettre de l’identifier au niveau du code.
Outre un ID, vous avez probablement remarqué qu’un contrôle dispose d’une valeur Name qui est souvent identique à ID. Sachez tout de suite que cela n’est en rien obligatoire.
Quelle est la différence entre Name et ID au juste ?
Name et ID peuvent parfaitement ne pas être fournis, tout dépend de ce que l’on veut faire avec le contrôle.
Pour donner une visibilité au contrôle depuis le code (client ou serveur), celui-ci doit disposer d’un ID. Sans cet ID le contrôle fonctionne normalement, mais n’est pas accessible.
C’est la principale fonction d’un ID, mais le W3C en propose d’autres :
• Servir de sélecteur de classe (la syntaxe avec le . (point) dont raffolent les infographistes et qui pourri la vie des développeurs)
• Servir de cible pour une ancre
• Nom d’un élément OBJECT
• Toutes autres fonctions définies par un agent comme un parseur…
Il faut également savoir que’ID et Name partagent le même espace de nom. Cela signifie qu’un élément ne peut avoir un ID identique au Name d’un élément distinct.
Le Name est lui indispensable pour indiquer à HTML que celui-ci doit renvoyer son contenu vers le serveur en cas de renvoi (postback).
Prenez le temps de tester l’exemple suivant car vous allez comprendre quelque chose d’important qui est au cœur du fonctionnement d’ASP.NET :
<body> <form id="form1" runat="server"> <input id="T1" type ="text" /> <input name="T3" id="T2" type="text" /> <asp:Button ID="Button1" runat ="server" Text="Button" OnClick="Button1_Click" /> </form> </body>
Deux zones de saisies, l’un dispose d’un attribut Name, l’autre pas.
Le code behind doit juste déclarer :
protected void Button1_Click(object sender, EventArgs e) { foreach (string Chaine in this.Page.Request.Form.AllKeys) { Response.Write(Chaine + ": " + this.Page.Request.Form[Chaine] + "<br/>"); } }
Le code se contente de vider les valeurs postées par le navigateur vers le serveur.
Une fois la page affichée, faites une saisie dans chaque composant puis cliquez sur le bouton. Quelque chose de similaire à ceci va s’afficher :
__VIEWSTATE: /wEPDwUKMTQ2OTkzNDNiJUgLECjTMAzZIAcnKHmgvbXo=
T3: salut
Button1: Button
__EVENTVALIDATION: /wEWAgLZ+OznCQKM54rGBq73LPgMkgevvUcn9O4MEN
• On constate bien que seule la saisie du inputBox avec le Name est effectivement retournée vers le serveur.
• Du même coup on touche du doigt le mécanisme qui est à la base du fonctionnement de la persistance des données et qui ne doit rien au viewstate comme on le pense souvent, c’est une propriété intrinsèque des navigateurs.
• Au passage on observe que la clef qui est renvoyée est Name et pas ID.
• Vous noterez également que Name n’a pas spécialement besoin de porter la même valeur que ID.
Notez également qu’ASP.NET ajoute automatiquement un Name à tous les contrôles serveurs de type zone de saisie (TextBox, mais pas Label).
Voyons comment un ID est généré. A priori les choses paraissent simples :
• Je pose un composant sur ma page
• Je lui donne un ID, Compo1 par exemple
• Et c’est terminé, ma page HTML exhibe un joli composant avec ID=Compo1
Oui, mais que se passe t’il si ce composant fait partie du template d’un GridView ? Il va être répété autant de fois qu’il y a de lignes. Est-ce à dire que ma page va disposer de plusieurs composants avec le même ID ? Pas terrible pour un identifiant unique !
En fait pas du tout.
Certains composants sont des composants conteneur. Un conteneur a pour propriété de créer un espace de nom d’ID destiné à ses contrôles enfants. Pour cela il préfixe les ID de ses enfants par son propre ID.
Si j’ajoute que GridViewRow (le composant qui implémente une ligne d’un GridView) est un composant conteneur, vous comprendrez facilement ce qui se passe et pourquoi mon composant garde un ID unique.
Dans le cas où vous souhaitez implémenter un composant conteneur vous disposez de deux méthodes :
1. Le faire hériter de CompositeControl
2. Lui faire implémenter l’interface INamingContainer
INamingContainer est une interface purement déclarative, vous n’avez aucune ligne de code à ajouter.
Notez que Panel n’implémente pas cette interface.
Un ID va typiquement ressembler à ceci vu depuis la page Web :
ctl00_ContentPlaceHolder1_ddlGrpAD
Le ctl initial est codé en dur, on ne peut pas le modifier. Le reste dépend de la hiérarchie de conteneurs rencontrée entre la racine de la page et le composant.
L’attribut Name prend la forme suivante :
ctl00$ContentPlaceHolder1$ddlGrpAD
Par défaut il est identique à l’ID, mais le délimiteur est $ et non pas _.
De tels noms peuvent rapidement devenir volumineux. Il n’est donc pas utile de décorer à torts et à travers vos contrôles avec INamingContainer si celui-ci n’entre pas dans une des catégories suivantes de composant :
• Le contrôle peut être lié à des données
• Le contrôle est un template
• Le contrôle contient des contrôles enfants et des événements doivent être routés vers les enfants
Il est possible d’obtenir la valeur générée pour ID depuis le code serveur en interrogeant la propriété ClientID. C’est cette valeur que vous passez au code client pour qu’il puisse atteindre votre contrôle.
La valeur UniqueID retourne quand à elle la valeur de Name. C’est celle que vous allez utiliser pour explorer la liste Request.Form afin de réhydrater vos composants créés dynamiquement par exemple.
Se pose alors la question : à quel moment UniqueID et ClientID sont calculés ?
Intuitivement c’est évident, puisque ces valeurs dépendent de la hiérarchie de contrôles au dessus de votre composant, ce ne peut être qu’au moment où il est inséré dans cette hiérarchie, c'est-à-dire dans la collection Controls de la Page.
L’insertion d’un contrôle dans cette collection déclenche automatiquement la méthode Control.ControlAdded() qui se charge de créer l’ID et d’autres choses encore.
Si vous avez besoin d’accéder au conteneur le plus proche du contrôle, il suffit d’appeler sa propriété NamingContainer.
Notez pour finir les deux propriétés Control.ClientIDSeparator et Control.IdSeparator qui retournent le délimiteur pour un ClientID et un UniqueID respectivement.
Même si son emploi est assez rare, on dispose de la méthode EnsureID() qui assure que tous les contrôles disposent d’un ID.
Bibliographie
1. Les Web SSO :
http://www.techheadbrothers.com/Articles.aspx/websso-internet-information-server-ja-sig-central-authentication-service
Aucun commentaire pour le moment
Laisser un commentaire
| « Un peu de tout | Qautre p’tits bugs… et puis s’en vont » |