Validering av checkboxlista

I ett skolprojekt som i detta nu håller på att bli färdigställt har jag gjort en enkät-funktionalitet där några av frågorna innebär att man kan kryssa i flera alternativ, men man måste kryssa i minst ett.

Checkboxar och Checkboxlistor går dock inte att validera med en vanlig RequiredFieldValidator eftersom en ej ikryssad checkbox faktiskt också är ett val. Så vi får ta till en CustomValidator och själva skriva kod både på serversidan och klientsidan (javascript) för att genomföra valideringen på ett säkert (serversidan) och användarvänligt (klientsidan) sätt.

Vi börjar med att fixa en fråga och en checkboxlista, så här ser aspx-koden ut:

Hur reser du till/från jobbet? (flera svar möjliga)<br />
<asp:CheckBoxList ID="cblWorkTravels" runat="server">
    <asp:ListItem>Åker bil</asp:ListItem>
    <asp:ListItem>Åker kommunalt</asp:ListItem>
    <asp:ListItem>Går</asp:ListItem>
    <asp:ListItem>Cyklar</asp:ListItem>
    <asp:ListItem>Annat</asp:ListItem>
</asp:CheckBoxList>

<asp:Button ID="btnReply" runat="server" Text="Svara" />

Vi kommer fokusera på valideringen här så vad som händer när man klickar på knappen är upp till dig, men validera kommer den att göra oavsett.

Nästa steg är att lägga till en CustomValidator och implementera dess ServerValidate-event. Metodsignaturen för det eventet ser ut så här;

protected void cvWorkTravels_ServerValidate(object source, ServerValidateEventArgs args)

Det andra argumentet, args, har en boolean property vid namn IsValid som är av särskilt intresse. Uppgiften är nu att skriva kod i metoden som sätter IsValid till true om valideringen ska anses lyckas, och annars till false;

protected void cvWorkTravels_ServerValidate(object source, ServerValidateEventArgs args)
{
    bool result = false;
    foreach (ListItem item in cblWorkTravels.Items)
    {
        if (item.Selected)
        {
            result = true;
            break;
        }
    }
    args.IsValid = result;
}

Så vi loopar igenom alla alternativ i checkboxlistan och så fort vi hittar ett alternativ som är markerat så avbryter vi loopen och sätter args.IsValid till true. Om inget alternativ är valt kommer args.IsValid att sättas till false.

Nu går det utmärkt att testa detta men den röda lilla stjärnan som indikerar att man gjort fel kommer att visas först efter en postback. Så kan vi inte ha det, vi måste förstås fixa ett javascript som validerar detta i klienten, särskilt om du kombinerar en CustomValidator med andra valideringskontroller som så fint fixar valideringen även på klientsidan.

Vår CustomValidator har en property vid namn ClientValidationFunction som ska innehålla namnet på den javascriptfunktion som gör valideringen. Vi sätter den till "validateWorkTravels" och infogar följande scriptblock i aspx-sidan;

function validateWorkTravels(sender, args)
{
    var result = false;
    for(var i = 0; i < <%=cblWorkTravels.Items.Count %>; i++)
    {
        var checkBox = $get('<%=cblWorkTravels.ClientID %>_' + i);
        if(checkBox.checked)
        {
            result = true;
            break;
        }
    }
    args.IsValid = result;
}

Logiken här är exakt densamma som på serversidan, men intressant att notera här är hur jag kommer åt själva checkboxelementen i javascript-koden.

Till att börja med måste jag säkerställa att jag inte loopar fler gånger än vad det finns checkboxar, det gör jag genom att hoppa över till serversidan och hämta ut antalet precis som om jag hade gjort det i C#-koden. Observera att du inte får någon intellisense från Visual Studio här, så var noga med stavning och små/stora bokstäver. Du kommer också få nån markering på serverscriptavgränsaren <%= men det är bara att ignorera.

Två rader ned accessar jag en checkbox via Asp.NET AJAX-genvägen $get (en ScriptManager har jag alltså lagt till i sidan) och där hämtar jag ut ClientID för checkboxlistan och lägger till _0, _1 etc för det är den namnkonvention som Asp.NET använder när den sätter ID på checkboxarna. De tre fetstilta raderna ovan renderas alltså ut till webbläsaren så här;

for(var i = 0; i < 5; i++)
{
    var checkBox = $get('cblWorkTravels_' + i);

Det fetstilta i detta exempel är det som kommer från <%= %>-avdelningarna i kodexemplet längre upp.

I det skarpa exemplet var det förstås så att det förekom flera frågor som fungerade på samma sätt. Det föranledde följande omarbetning av koden på serversidan;

protected void cvWorkTravels_ServerValidate(object source, ServerValidateEventArgs args)
{
    args.IsValid = IsAnyChecked(cblWorkTravels);
}

private bool IsAnyChecked(CheckBoxList cbl)
{
    bool result = false;
    foreach (ListItem item in cbl.Items)
    {
        if (item.Selected)
        {
            result = true;
            break;
        }
    }
    return result;
}

Nu kan vi lägga till fler Checkboxlistor som har varsitt ServerValidate-event som i sin tur anropar metoden IsAnyChecked. Här följer hela den slutgiltiga aspx-koden med samma förändring i javascriptdelen;

<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <div>    
        Hur reser du till/från jobbet? (flera svar möjliga)
        <asp:CustomValidator ID="cvWorkTravels" runat="server" 
            Text="*" onservervalidate="cvWorkTravels_ServerValidate" 
            ClientValidationFunction="validateWorkTravels" />
        <br />
        <asp:CheckBoxList ID="cblWorkTravels" runat="server">
            <asp:ListItem>Åker bil</asp:ListItem>
            <asp:ListItem>Åker kommunalt</asp:ListItem>
            <asp:ListItem>Går</asp:ListItem>
            <asp:ListItem>Cyklar</asp:ListItem>
            <asp:ListItem>Annat</asp:ListItem>
        </asp:CheckBoxList>
        
        <asp:Button ID="btnReply" runat="server" Text="Svara" />
    </div>
    </form>
    <script type="text/javascript">
        //<![CDATA[

        function validateWorkTravels(sender, args)
        {
            args.IsValid = IsAnyChecked('<%=cblWorkTravels.ClientID %>', <%=cblWorkTravels.Items.Count %>);
        }
        
        function IsAnyChecked(cblClientID, cblLength)
        {
            var result = false;
            for(var i = 0; i < cblLength; i++)
            {
                var checkBox = $get(cblClientID + '_' + i);
                if(checkBox.checked)
                {
                    result = true;
                    break;
                }
            }
            return result;            
        }
            
        //]]>
    </script>  
</body>

En skillnad i javascriptet blir att metoden IsAnyChecked får ta emot både ID:t för checkboxlistan, samt dess längd eftersom det är något jag inte kan få ut på klientsidan.

No comments :

Post a Comment