Kattints, de csak egyszer! Action!

Aki készített már adatkezelő weblapot átlagfelhasználók számára, biztosan találkozott már azzal a feladattal, hogy biztosítani kellett, hogy a felhasználó csak egyszer küldhesse el a kitöltött űrlapot feldolgozásra. Az újraküldés figyelésére általánosan fogalmazva két lehetőség van:

  • Újraküldés figyelése szerver oldalon. Ez nagyon kényelmes, szerver oldali programozást biztosít, de nem egyszerű feladat és könnyen előfordulhat, hogy űrlap típusonként más megoldást kell alkalmaznunk. Az újraküldést ellenőrző logika ráadásul látványosan elcsúnyítja az adatok feldolgozását végző kódot.
  • Újraküldés tiltása kliens oldalon. Ez a megoldás lényegesen jobban kezelhető felhasználói felületet eredményez, nehézsége, hogy JavaScriptet kell írni hozzá és persze nem bolondálló a megoldás (nem árt, ha ettől függetlenül ellenőrzünk még egyszer szerver oldalon is).

A jobb felhasználói élmény, a kód újrafelhasználhatósága és a szerver tehermentesítése érdekében sokan választják a második megoldást. A feladat többféleképpen megoldható, nézzünk most egy újkeletű megoldást, használjuk az ASP.NET AJAX Control Toolkitet!

Az egyszerű példa kedvéért korlátuzzuk a feladatot arra, hogy egy gombot kell letiltanunk abban az esetben, ha a felhasználó rákattint. Hozzunk létre egy új AJAX Control Toolkit Web Site típusú projektet Visual Studioban, majd dobjunk a generált Default.aspx oldalra egy TextBox (ID=txtName), egy Label (ID=lblResult) és egy Button (ID=btnGo) kontrollt.

    <asp:TextBox ID="txtName" runat="server">Gipsz Jakab</asp:TextBox>
    <asp:Button ID="btnGo" runat="server" Text="Elküldés" OnClick="btnGo_Click" />
    <asp:Label ID="lblResult" runat="server" />

Írjunk a gombhoz egy rövid eseménykezelőt, amelyben kissé belassítjuk az oldal végrehajtását:

    protected void btnGo_Click( object sender, EventArgs e )
    {
        System.Threading.Thread.Sleep( 5000 );
        this.lblResult.Text += String.Format( "Köszönjük, {0}!", this.txtName.Text );
    }

Az újraküldés elkerülésére használjuk a Control Toolkitben lévő AnimationExtender kontrollt! Dobjunk belőle egy példányt az oldalra és állítsuk be úgy, hogy a gombunk funkcionalitását terjessze ki (TargetControlID="btnGo").

Sajnos a kód többi részét kézzel kell megírnunk, ezért váltsunk át markup forráskód nézetre. Az AnimationExtender tag-en belül hozzünk létre egy Animations elemet, ez fogja tartalmazni az animációkat, majd azon belül egy OnClick elemet – itt lesznek a kattintáskor lefutó animációk:

    <ajaxToolkit:AnimationExtender ID="ae" runat="server" TargetControlID="btnGo">
        <Animations>
            <OnClick>
            </OnClick>
        </Animations>
    </ajaxToolkit:AnimationExtender>

A lehetséges effektusoknak két fajtája létezik a Control Toolkitben:

  • Az animációk valaminek az értékét változtatják egy kezdőállapottól egy végállapotig a megadott időtartam alatt. Ilyen például a mozgatás (Move), az átméretezés (Resize) vagy a halványítás (FadeIn, FadeOut), melyek az Animation ősosztályból származnak.
  • Lényegében az animációk speciális esetének tekinthetők az akciók, melyek az Animationből származó Action osztály gyermekei. Az akciók olyan animációk, amelyek valójában nem is animálnak, hanem egyszerűen csak megtörténnek egy pillanat alatt. Ilyen például egy kontroll elrejtése vagy megjelenítése (Hide), átlátszóságának állítása (Opacity), valamilyen kód futtatása (Script) vagy éppen egy kontroll engedélyezése vagy tiltása (Enable).

A mi célunknak remekül megfelel az utolsóként említett EnableAction, amivel lehet engedélyezni vagy tiltani egy kontrollt. Adjuk hozzá az OnClick elemhez ezt az "animációt":

    <EnableAction Enabled="false" />

A StyleAction segítségével megváltoztathatjuk az adott kontroll stílusát, például elküldés esetén a homokóra egérkurzort rendelhetjük hozzá. Ehhez az alábbi sort kell csak beírnunk az EnableAction elem helyett:

    <StyleAction Attribute="cursor" Value="wait"/>

Hogy még feltűnőbb legyen a felhasználó számára, hogy történik valamit, kattintás esetén még a gomb szövegét is átírhatjuk. Erre elméletben remek lenne a PropertyAnimation, nekem viszont nem sikerült működésre bírnom a januári CTP változattal. Sebaj, legalább megnézhetjük, hogyan használható a ScriptAction! Ebben az esetben nekünk kell megírnunk azt a minimális kódot, ami a feladatot elvégzi, szerencsére használhatjuk a Microsoft AJAX Library metódusait, például a $get segítségével elkérhetünk egy a gombra mutató referenciát:

    <ScriptAction Script="$get('btnGo').value='Türelem...';"/>

Szerencsére alkalmazhatjuk egyszerre mindhárom animációt is, ekkor azonban be kell ágyaznunk őket egy Sequence vagy Parallel elembe, valahogy így:

    <ajaxToolkit:AnimationExtender ID="ae" runat="server" TargetControlID="btnGo">
        <Animations>
            <OnClick>
                <Sequence>
                    <EnableAction Enabled="false" />
                    <StyleAction Attribute="cursor" Value="wait"/>
                    <ScriptAction Script="$get('btnGo').value='Türelem...';"/>
                </Sequence>
            </OnClick>
        </Animations>
    </ajaxToolkit:AnimationExtender>

Hát nem nagyszerű?! Megint nem írtunk egyetlen sornyi JavaScript kódot sem és van egy remélhetőleg cross-browser megoldásunk – annyira az, amennyire az AnimationExtender, tehát éppen eléggé smile_wink!

Mindez nagyon szép, de mi van akkor, ha egy UpdatePanelt szeretnénk frissíteni? Gond egy szál se, akkor használjuk az UpdatePanelAnimationExtender kontrollt, amely szintén az AJAX Control Toolkit része. Ez annyiban más, hogy OnClick helyett OnUpdating és OnUpdated eseményei és elemei vannak, melyek a frissítés előtt és után futnak le. Itt már gondoskodnunk kell arról, hogy az elküldés előtt módosított értékeket a válasz megérkezése után visszaállítsuk, azaz újra engedélyezzük a kontrollt. Ez az EnableAction és a StyleAction esetén nem annyira bonyolult:

    <ajaxToolkit:UpdatePanelAnimationExtender ID="ae" runat="server" TargetControlID="up">
        <Animations>
            <OnUpdating>
                <Sequence>
                    <EnableAction Enabled="false" />
                    <StyleAction Attribute="cursor" Value="wait"/>
                </Sequence>
            </OnUpdating>
            <OnUpdated>
                <Sequence>
                    <EnableAction Enabled="true" />
                    <StyleAction Attribute="cursor" Value="hand"/>
                </Sequence>
            </OnUpdated>
        </Animations>
    </ajaxToolkit:UpdatePanelAnimationExtender>

A gond a gomb kezelésénél van. Először is az UpdatePanelAnimationExtender az UpdatePanel vezérlőt terjeszti ki (lásd fent TargetControlID="up"), nem a gombot. Tehát ha a gomb szövegét akarjuk megváltoztatni, akkor vagy az AnimationTarget tulajdonságot kell használnunk, vagy ésszel kell megírnunk a ScriptActiont. Az utóbbi esetben az jelenti a nehézséget, hogy elküldés előtt el kell tárolnunk a gomb eredeti szövegét, amit majd vissza kell állítanunk a válasz megérkezése után. Ehhez használjuk az extender BehaviorID tulajdonságát, amiről a dokumentáció mindössze ennyit ír:

BehaviorID: In cases where you would like to access the client-side behavior for your extender from script code in the client, you can set this BehaviorID to simplify the process.

Ezt felhasználva a teljes kontroll felparaméterezve így fest:

    <ajaxToolkit:UpdatePanelAnimationExtender ID="ae" BehaviorID="animation" 
runat="server" TargetControlID="up"> <Animations> <OnUpdating> <Sequence> <EnableAction Enabled="false" /> <StyleAction Attribute="cursor" Value="wait"/> <ScriptAction Script="var a = $find('animation');
var b = $get('btnGo');
a._originalText=b.value;
b.value='Türelem...';" />
</Sequence> </OnUpdating> <OnUpdated> <Sequence> <EnableAction Enabled="true" /> <StyleAction Attribute="cursor" Value="hand"/> <ScriptAction Script="$get('btnGo').value=$find('animation')._originalText;"/> </Sequence> </OnUpdated> </Animations> </ajaxToolkit:UpdatePanelAnimationExtender>

Nos, az eredmény talán bonyolultabb, mint azt előtte sejtettük, sőt, talán nem ez a létező legegyszerűbb megoldás, de az biztos, hgoy nem kellett egyetlen sort sem kódolnunk a kliens oldalra és a kód egészen áttekinthető! Az arany középút valószínűleg az EnableAction és az UpdateProgress kontroll használata.

A teljes forráskód letölthető a devPORTAL információtárából, jó kísérletezést!

Technorati tags: ,

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s