Klient-id:n i ASP.NET 4.0

När man arbetar med JavaScript och Web Forms-baserade ASP.NET-sidor så uppstår ofta problem när man skall plocka ut ett element som har genererats genom en kontroll. Problemet beror på att id:t som genereras hos klienten ser ut på ett sätt som säkerställer att det alltid är unikt.

För att kunna använda JavaScript för att anropa ett element vars id genereras på serversidan så brukar man göra något i stil med det här:

document.getElementById(’<%=Kontrollen.ClientId%>’);

Det kräver dock att man har koden på samma sida som kontrollen, och fungerar således inte i js-filer, där den hör hemma.

En nyhet i ASP.NET 4.0 är att man själv kan avgöra vilket id som skall genereras hos klienten. Genom en ny property vid namn ”ClientIDMode” så kan man ange fyra olika värden – Inherit, Legacy, Predictable samt Static.

För att kunna visa hur de olika värdena fungerar så kommer jag att använda den här sidan för att skapa upp element:

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ClientId._Default" %>
<%@ Register Src="Controls.ascx" TagName="Controls" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>ClientId Test</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:Panel ID="pnlItems" runat="server">
        <uc1:Controls ID="Controls1" Text="Testar" runat="server" />
        <br />
        <asp:GridView ID="gvNames" ClientIDMode="Legacy" runat="server" AutoGenerateColumns="false" CellPadding="4"
            ForeColor="#333333" GridLines="None">
            <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
            <Columns>
                <asp:TemplateField HeaderText="Name">
                    <ItemTemplate>
                        <uc1:Controls ID="Controls1" Text='<%#Eval("Value") %>' runat="server" />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
            <EditRowStyle BackColor="#999999" />
            <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
            <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />
            <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
            <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
        </asp:GridView>
    </asp:Panel>
    </form>
</body>
</html>

Default.aspx.cs

using System;
using System.Collections.Generic;
 
namespace ClientId
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Dictionary<int, string> users = new Dictionary<int, string>();
            users.Add(100, "Mikael");
            users.Add(200, "Steve");
            users.Add(300, "Bill");
 
            gvNames.DataSource = users;
            gvNames.DataBind();
        }
    }
}

Controls.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Controls.ascx.cs" Inherits="ClientId.Controls" %>
<asp:Label ID="lblName" runat="server" />

Controls.ascx.cs

using System;
using System.Web.UI;
 
namespace ClientId
{
    public partial class Controls : UserControl
    {
        public string Text { get; set; }
 
        protected void Page_Load(object sender, EventArgs e)
        {
            lblName.Text = Text;
        }
    }
}

Det som visas är dels en fristående Controls-kontroll, samt även en GridView som använder kontrollen för alla namn som binds till den. Anledningen till att det är så många kontroller i exemplet är för att kunna visa på ett bra sätt hur de genererade elementen ser ut.

Inherit

Inherit betyder att kontrollen ärver värdet från kontrollen innan. Om vi har Legacy i en kontroll (t.ex. PlaceHolder) och Inherit på kontrollerna under (vilket är standard), så betyder det att kontrollerna i PlaceHolder-kontrollen också har Legacy. Om inget anges på någon kontroll så ärvs det automatiskt från Page.

Legacy

För att få allt att fungera som standard så använder vi Legacy. Det här är standard på alla sidor, och därmed alla kontroller i dessa. Det här genererar alla id:n precis som i .NET 1.0-3.5.

Resultat:

<div id="Div1">
    <span id="Controls1_lblName">Testar</span>
    <br />
    <div>
    <table cellspacing="0" cellpadding="4" border="0" id="Table1" style="color:#333333;border-collapse:collapse;">
        <tr style="color:White;background-color:#5D7B9D;font-weight:bold;">
            <th scope="col">Name</th>
        </tr><tr style="color:#333333;background-color:#F7F6F3;">
            <td>
                    <span id="gvNames_ctl02_Controls1_lblName">Mikael</span>
                </td>
        </tr><tr style="color:#284775;background-color:White;">
            <td>
                    <span id="gvNames_ctl03_Controls1_lblName">Steve</span>
                </td>
        </tr><tr style="color:#333333;background-color:#F7F6F3;">
            <td>
                    <span id="gvNames_ctl04_Controls1_lblName">Bill</span>
                </td>
        </tr>
    </table>
</div>

Det kan här bli svårt att veta hur id:t skall hämtas med JavaScript då det är helt beroende på kontrollerna utanför. Om man byter namn på en kontroll, alternativt flyttar runt dem så blir det ett annorlunda id.

Predictable

När vi använder kontroller som visar upp en samling av data, som t.ex. Repeater eller GridView så bör man använda Predictable eller Legacy. Just Predictable lägger automatiskt till en siffra på slutet av alla id:n för att säkerställa att vi inte får flera element med samma id.

Resultat:

<div id="Div1">
    <span id="Controls1_lblName">Testar</span>
    <br />
    <div>
        <table cellspacing="0" cellpadding="4" border="0" id="Table1" style="color:#333333;border-collapse:collapse;">
            <tr style="color:White;background-color:#5D7B9D;font-weight:bold;">
                <th scope="col">Name</th>
            </tr><tr style="color:#333333;background-color:#F7F6F3;">
                <td>
                        <span id="gvNames_Controls1_0_lblName_0">Mikael</span>
                    </td>
            </tr><tr style="color:#284775;background-color:White;">
                <td>
                        <span id="gvNames_Controls1_1_lblName_1">Steve</span>
                    </td>
            </tr><tr style="color:#333333;background-color:#F7F6F3;">
                <td>
                        <span id="gvNames_Controls1_2_lblName_2">Bill</span>
                    </td>
            </tr>
        </table>
    </div>
</div>

Här kan vi se en liten skillnad. Här försöker ASP.NET generera id:n som görs unika genom att lägga till en unik siffra efter kontrollens id. Det underlättar en del då vi kan styra namnen bättre. Det går även att utöka detta för att få ännu mer kontroll, men mer om det senare.

Static

Om vi vill använda exakt det id:t som vi satte på kontrollen så kan vi använda static. Om kontrollens id är ”MinKontroll” på servern, så blir det även exakt det på klienten. Det här kan dock leda till att flera kontroller får samma id, vilket kräver at utvecklaren är uppmärksam.

Resultat:

<div id="Div1">
    <span id="lblName">Testar</span>
    <br />
    <div>
        <table cellspacing="0" cellpadding="4" border="0" id="Table1" style="color:#333333;border-collapse:collapse;">
            <tr style="color:White;background-color:#5D7B9D;font-weight:bold;">
                <th scope="col">Name</th>
            </tr><tr style="color:#333333;background-color:#F7F6F3;">
                <td>
                        <span id="lblName">Mikael</span>
                    </td>
            </tr><tr style="color:#284775;background-color:White;">
                <td>
                        <span id="lblName">Steve</span>
                    </td>
            </tr><tr style="color:#333333;background-color:#F7F6F3;">
                <td>
                        <span id="lblName">Bill</span>
                    </td>
            </tr>
        </table>
    </div>
</div>

Här kan vi se att alla id:n får samma värde. Det här leder till att koden inte validerar och att vi inte kan använda getElementById(). Det bör absolut inte användas i kontroller som visar upp repeterande kontroller, utan bara enskilda element.

Mer kontroll över id:n i databundna kontroller

För att få mer kontroll över id:n i databundna kontroller som GridView så finns det en ny property som kan användas om vi har satt ClientIDMode till Predictable. Som jag visade i exemplet tidigare så används dels ett prefix i form av kontrollens id, samt även ett suffix i form av en siffra. Med hjälp av en property vid namn ”ClientIDRowSuffix” så kan vi ändra suffixet till ett eget värde.

Vi kan här använda ett eller flera fält från vår datakälla. Om vi istället för att ha 1, 2, 3 osv som suffix vill ha ”personensId_personensNamn” så kan vi sätta propertyn i vår GridView till ”Key,Value” vilket är våra två properties som vi har bundit.

Resultat blir då:

<table cellspacing="0" cellpadding="4" border="0" id="Table1" style="color:#333333;border-collapse:collapse;">
    <tr style="color:White;background-color:#5D7B9D;font-weight:bold;">
        <th scope="col">Name</th>
    </tr><tr style="color:#333333;background-color:#F7F6F3;">
        <td>
                <span id="gvNames_Controls1_100_Mikael_lblName_100_Mikael">Mikael</span>
            </td>
    </tr><tr style="color:#284775;background-color:White;">
        <td>
                <span id="gvNames_Controls1_200_Steve_lblName_200_Steve">Steve</span>
            </td>
    </tr><tr style="color:#333333;background-color:#F7F6F3;">
        <td>
                <span id="gvNames_Controls1_300_Bill_lblName_300_Bill">Bill</span>
            </td>
    </tr>
</table>

Vi har här fått t.ex. ”100_Mikael” som suffix istället för ”1”. Anledningen till att det kommer på två ställen beror på att vi inkluderar en user control med en label som visar namnet. Hade vi skrivit ut namnet direkt på sidan så hade vi bara fått upp det en gång.

5 Comments

Comments have been disabled for this content.