Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.
Dzisiaj porozmawiamy sobie o podstawach javascript, o bibliotece Microsoft AJAX, pokażemy jak użyć jej po stronie klienta do kontroli serwera :)
Tworzenie własnego skryptu
Istnieją trzy podstawowe “modele” definiowania skryptów:
- zdefiniować kod skryptu po stronie klienta na witrynie sieci WEB, lub atrybut odniesienia do gotowego pliku (.js) .
- użyć klasy ClientScriptManager do dynamicznego definiowania skytpów.
- użyć kontrolki ScriptManager do zarejestrowania javascipt na naszej stronie.
Każdy ze sposobów opiszemy sobie poniżej.
Dodawanie bloków skryptu do naszej strony ASP.NET
Jak wspomniałem wcześniej w asp.net tradycyjnie na stronie możemy umieścić kod javascript na stronie bądź dołączyć plik (.js) tak jak to chyba najczęściej się stosuje. Korzystamy z tego sposobu kiedy nie musimy dynamicznie tworzyć skryptu na podstawie procesów zachodzących na stronie. Poniżej przykład strony która ukrywa wartość lub ja pokazuje w zależności od kliknięć w przycisk. Jako mega ciekawostkę mogę powiedzieć, że na IE skrypt ten działa na Firefox już nie :/ Ale to już odwieczny ból z css i js…trzeba wszędzie przetestować i dostosować ;) Ok wrzucam poniżej kod + skrypt:
1: <body style="font-family: Verdana;">
2: <form id="form1" runat="server">
3: <div>
4: <div style="width: 200px; background-color: Blue; color: White;
5: border-style: solid; border-width: thin; border-color: Blue">
6: <div style="float: left; vertical-align: middle; margin-top: 3px;">
7: Element Title
8: </div>
9: <div style="float: right; vertical-align: middle">
10: <input id="ButtonCollapse" type="button" value="Close"
11: onclick="Collapse()" />
12: </div>
13: </div>
14: <div id="DivCollapse" style="width: 200px; height: 200px;
15: border-style: solid; border-width: thin; border-color: Blue">
16: <div style="margin-top: 20px; text-align: center;">
17: Content area ...
18: </div>
19: </div>
20: </div>
21: </form>
22: </body>
1: <head id="Head1" runat="server">
2: <title>Script Block</title>
3: <script language="javascript" type="text/javascript">
4: function Collapse()
5: {
6: if (DivCollapse.style.display == "")
7: {
8: DivCollapse.style.display = "none";
9: document.forms[0].ButtonCollapse.value = "Open";
10: }
11: else
12: {
13: DivCollapse.style.display = "";
14: document.forms[0].ButtonCollapse.value = "Close";
15: }
16: }
17: </script>
18: </head>
Powyższy przykład pokazuje kod osadzony na stronie. Ale kiedy istnieje konieczność wykorzystania tego samego skryptu na wielu stronach należy zapisać go do w pliku .js a następnie na każdej z nich dołączyć go w następujący sposób:
1: <script type="text/javascript" src="SiteScripts.js"></script>
W ten sposób nie powielamy kod i zyskujemy na wydajności gdyż skrypt jest buforowany przez przeglądarkę.
Dynamiczne dodawanie skryptów
Musimy być przygotowani na to, że zaistnieje konieczność tworzenia dynamicznie skryptu, którego kod determinują działania wykonywane na stronie. Do tego celu możemy użyć klasy ClientScriptManager. Aby dodać skrypt musimy mieć go zdefiniowanego w pliku lub jako string. Następnie wywołujemy metodę RegisterClientScriptBlock obiektu ClientScript do której przekazujemy typ, klucz który jednoznacznie identyfikuje skrypt, sam skrypt oraz na koniec wartość typu boolean która określa czy jest potrzeba generowania tagów skryptu czy też nie.
Załóżmy, że chcieli byśmy umożliwić użytkownikowi na stronie sprawdzenie poziomu trudności wpisanego hasła. Poniższy kod realizuje to zadanie na życzenie użytkownika, czyli jeżeli zaznaczy chcekboxa wygeneruje się dynamicznie kod javascript. Treść html:
1: <body style="font-family: Verdana">
2: <form id="form1" runat="server">
3: <div>
4: Enter Password<br />
5: <asp:TextBox ID="TextBox1" runat="server" Width="250"></asp:TextBox>
6: <span id="passwordStrength"></span>
7: <br />
8: <asp:CheckBox ID="CheckBox1" runat="server"
9: Text="Turn on password strength checking" AutoPostBack="true" />
10: </div>
11: </form>
12: </body>
Oraz skrypt:
1: //C#
2: public partial class DynamicScriptC : System.Web.UI.Page
3: {
4: protected void CheckBox1_CheckedChanged(object sender, EventArgs e)
5: {
6: if (CheckBox1.Checked)
7: {
8: string passFunc = "function CheckPassword() {";
9: passFunc += @"var passLen = document.forms[0].TextBox1.value.length;";
10: passFunc += @" if (passLen < 4) {";
11: passFunc += @" document.getElementById(""passwordStrength"").";
12: passFunc += @"innerText = ""weak"";";
13: passFunc += @" document.getElementById(""passwordStrength"").";
14: passFunc += @"style.color = ""red"";}";
15: passFunc += @" else if (passLen < 6) {";
16: passFunc += @" document.getElementById(""passwordStrength"").";
17: passFunc += @"innerText = ""medium"";";
18: passFunc += @" document.getElementById(""passwordStrength"").";
19: passFunc += @"style.color = ""blue"";}";
20: passFunc += @" else if (passLen > 9) {";
21: passFunc += @" document.getElementById(""passwordStrength"").";
22: passFunc += @"innerText = ""strong"";";
23: passFunc += @" document.getElementById(""passwordStrength"").";
24: passFunc += @"style.color = ""green"";}}";
25: //register the script
26: Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
27: "CheckPasswordScript", passFunc, true);
28: //add an event to the text box to call the script
29: TextBox1.Attributes.Add("onkeyup", "CheckPassword()");
30: }
31: else
32: {
33: //remove the event from the text box
34: TextBox1.Attributes.Remove("onkeyup");
35: }
36: }
37: }
Użycie kontrolki ScriptManager
Kontrolka ta jest używana do ASP.NET AJAX Extension. Automatycznie rejestruje odpowiednie pliki skryptów zdefiniowane przez bibliotekę MS AJAX. Ponadto możemy dodać własne skrypty, deklaratywnie oraz programowo. Poniżej krótkie przykłady:
1: <asp:ScriptManager ID="ScriptManager1" runat="server">
2: <Scripts>
3: <asp:ScriptReference Name="AppScripts.js" />
4: </Scripts>
5: </asp:ScriptManager>
1: //C#
2: ScriptReference sr = new ScriptReference("AppScripts.js");
3: ScriptManager1.Scripts.Add(sr);
Tworzenie własnego Client Callbacks
W poprzedniej lekcji dowiedzieliśmy się co nieco o update panelach. Dzięki niemu możemy aktualizować jakiś fragment na stronie. Kontrolka ta tworzy proste wywołanie zwrotne. Ale może się zdarzyć, że będziemy potrzebować większej kontroli nad sposobem połączenia klienta z serwerem i vice versa. W tym wypadku pozostaje nam napisać własne asynchroniczne wywołanie zwrotne. Aby utworzyć stronę z własnym kodem callback musimy wykonać następujący standardowy zbiór instrukcji. Po pierwsze, musimy określić kod po stronie serwera. Aby to zrobić, należy wykonać następujące kroki:
- zaimplementować System.Web.UI.ICallbackEventHandler na naszej stronie asp.net. Ten interfejs po stronie serwera pozawala zarówno odbierać jak i zwracać dane.
- zaimplementować metodę RaiseCallbackEvent interfejsu ICallbackEventHandler. Metoda taj jest wywoływana przez klienta. Można go używać do otrzymywania wartości parametru od klienta.
- zaimplementować metode GetCallbackResult interfejsu ICallbackEventHandler. Metoda ta wykorzystywana jest do zwrócenia wartości po przetwarzaniu przez serwer.
Gdy już mam kod po stronie serwera musimy teraz napisać stronę kliencką.
- zaimplementować GetCallbackEventReference do przetwarzania wyników pochodzących z serwera.
- wykorzystanie metody o której wczesniej była mowa RegisterClientScriptBlock
- ostatnia potrzebna funkcja aby stworzyć własne wywołanie jest generowana przez środowisko asp.net przy wywoływaniu GetCallbackEventReference
Załóżmy, że mamy na stronie kontrolkę DropDownList. Chcemy połączyć się z serwerem podczas wyboru jakiegoś elementu z listy przez użytkownika ale nie chcemy przeładowywać całej strony. Następnie przetworzyć dane na serwerze i zwrócić wynik na stronę. No to do dzieła ;) Musimy na początku zaimplementować ICallbackEventHandler i RaiseCallbackEvent.
1: public partial class Default2 :
2: System.Web.UI.Page, System.Web.UI.ICallbackEventHandler
1: //C#
2: string _callbackArgs;
3: public void RaiseCallbackEvent(string eventArgument)
4: {
5: _callbackArgs = eventArgument;
6: }
W RaiseCallbackEvent przekażemy stringa który będzie po prostu nazwą oferty z naszej listy rozwijanej. Oraz możemy zaimplementować GetCallbackResult metodę która zwróci nam wybraną przez nas ofertę ;)
1: //C#
2: public string GetCallbackResult()
3: {
4: return _callbackArgs;
5: }
Po stronie klienta musimy napisać skrypt, “zarejestrować” go i oczywiście zrobić odpowiedni szkielet strony dla naszych potrzeb:
1: //C#
2: protected void Page_Load(object sender, EventArgs e)
3: {
4: //register the name of the client-side function that will
5: // be called by the server
6: string callbackRef = Page.ClientScript.GetCallbackEventReference(
7: this, "args", "ClientCallbackFunction", "");
8: //define a function used by the client to call the server
9: string callbackScript = "function MyServerCall(args)" +
10: "{" + callbackRef + "; }";
11: //register the client function with the page
12: Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
13: "MyServerCall", callbackScript, true);
14: }
1: <script type="text/javascript"> 1:
2: function ClientCallbackFunction(args)
3: {
4: LabelMessage.innerText = args;
5: }
</script>
2: <asp:DropDownList ID="DropDownListChoice"
3: runat="server"
4: OnChange="MyServerCall(DropDownListChoice.value)">
5: <asp:ListItem>Choice 1</asp:ListItem>
6: <asp:ListItem>Choice 2</asp:ListItem>
7: <asp:ListItem>Choice 3</asp:ListItem>
8: </asp:DropDownList>
9: <br /><br />
10: <asp:Label ID="LabelMessage" runat="server"></asp:Label>
Praca z biblioteką ASP.NET AJAX
Praca z javascript, brak obiektowość itd. nie jest proste szczególnie przy mocno rozbudowanym oprogramowaniu. MIcrosoft stara się tworzyć bibliotekę która obudowuje javascript przez co praca z nim staje sie przyjemniejsza. Wykorzystując asp.net ajax na stronie musimy pamiętać o dołączeniu na stronę ScriptManager który zarządza które pliki trzeba dołączyć itd. Bez dołączenia ScriptManagera bądź jawnego dodania go w inny sposób biblioteka ms ajax nie zadziała.
Przez takie obudowanie javascriptu przez Microsoft zyskujemy nowe możliwości, zalety. Jakie? Np. wspieranie obiektowości, tryb debugowania, pełna kompatybilność, klasy bazowe i wiele wiele innych. W training kit są opisane pokrótce te zalety oraz przestrzenie nazw z jakich możemy skorzystać i co się w nich znajduje…mowa tutaj m.in o Global, Sys, Sys.Net, Sys.UI…. Ja jednak chciałbym teraz chwile zatrzymać się na obiektowości.
Javascript ma wsparcie dla podstawowych klas, funkcji, typów danych, operatorów i tym podobne. Microsoft Ajax Library pozwala nam dodatkowo skorzystać z dobrodziejstw interfejsów, dziedziczenia, zdarzeń, czy nawet refleksji. Nasuwa się pytanie czy potrzebne są nam te wszystkie elementy. Oczywiście nie zawsze ale przy rozbudowanej kontroli klienta, rozbudowanej aplikacji jak najbardziej.
Klasy (konstruktor, pola, właściwości i metody)
Składnią do utworzenia klasy z biblioteki jest PrzestrzeńNazwy.Klasa. Również można przypisać nazwę klasy do funkcji. Załóżmy, że chcemy dodać klasę do wcześniej określonej przestrzeni nazw Contoso.Utilities. Nazwijmy ją nazwą mówiącą samą za siebie ChangePasswordValidator. Oto kod:
1: //define class name (as function), create constructor, and
2: // set class-level field values
3: Contoso.Utilities.ChangePasswordValidator =
4: function(requirePasswordsNotMatch, requireNumber)
5: {
6: Contoso.Utilities.ChangePasswordValidator.initializeBase(this);
7: this.RequirePasswordsNotMatch = requirePasswordsNotMatch;
8: this.RequireNumber = requireNumber;
9: this._passwordRuleViolations = new Array();
10: }
Widzimy tutaj utworzoną klasę przy pomocy funkcji której parametry tworzą konstruktor. Po określeniu klasy i jej konstruktora wypadało by rozbudować ją o jakieś pola, właściwości czy metody.
Pola:
1: //define class contents (fields, properties, methods)
2: Contoso.Utilities.ChangePasswordValidator.prototype =
3: {
4: //declare fields
5: RequirePasswordsNotMatch: Boolean,
6: RequireNumber: Boolean,
7: ...
Jeśli chodzi o właściwości do javascript nie wspiera sam w sobie takich właściwości o jakich myślimy. Ale nic nie stoi na przeszkodzie żebyśmy sami stworzyli metody których działanie imitowało by właściwości.
1: //properties
2: set_currentPassword: function(value)
3: {
4: this._currentPassword = value;
5: },
6: set_changeToPassword: function(value)
7: {
8: this._changeToPassword = value;
9: },
10: get_passwordRuleViolations: function()
11: {
12: return this._passwordRuleViolations;
13: },
I przykład implementacji metod:
1: //methods
2: CheckPasswordStrength: function(password)
3: {
4: var strPass = new String(password.toString());
5: if (strPass.length < 4)
6: {
7: return Contoso.Utilities.PasswordStrength.Weak;
8: }
9: else if (strPass.Length < 7)
10: {
11: return Contoso.Utilities.PasswordStrength.Medium;
12: }
13: else
14: {
15: return Contoso.Utilities.PasswordStrength.Strong;
16: }
17: },
18: AllowPasswordChange: function()
19: {
20: var pass1 = new String(this._currentPassword);
21: var pass2 = new String(this._changeToPassword);
22: //use new, extended Array type
23: var ruleViolations = new Array();
24: //min length rule
25: if (pass2.length < 5)
26: {
27: Array.add(ruleViolations, 'Password too short.');
28: }
29: //check if passwords match
30: if (this.RequirePasswordsNotMatch)
31: {
32: if (pass1 == pass2)
33: {
34: Array.add(ruleViolations, 'Passwords cannot match.');
35: }
36: }
37: //contains numbers
38: if (this.RequireNumber)
39: {
40: if (pass2.match(/\d+/) == null)
41: {
42: Array.add(ruleViolations, 'Password must include a number.');
43: }
44: }
45: //reset rule violations property
46: this._passwordRuleViolations = ruleViolations;
47: //determine if change allowed
48: if (ruleViolations.length > 0)
49: {
50: return false;
51: }
52: else
53: {
54: return true;
55: }
56: }
57: }
Jak użyć stworzonej przez nas przed chwilą klasy?
Dodajemy ScriptManager’a do naszej strony i ustawiamy referencje do naszego pliku .js
1: <asp:ScriptManager ID="ScriptManager1" runat="server">
2: <Scripts>
3: <asp:ScriptReference path="ContosoUtilities.js" />
4: </Scripts>
5: </asp:ScriptManager>
A następnie wykorzystywać wszystkie poprzednio zdefiniowane metody:
1: <script language="javascript" type="text/javascript"> 1:
2: //call constructor
3: var validator =
4: new Contoso.Utilities.ChangePasswordValidator(true, true, true);
5: //check the password strength
6: strength = validator.CheckPasswordStrength("password");
7: switch (strength)
8: {
9: case Contoso.Utilities.PasswordStrength.Weak:
10: alert("Weak");
11: break;
12: case Contoso.Utilities.PasswordStrength.Medium:
13: alert("Medium");
14: break;
15: case Contoso.Utilities.PasswordStrength.Strong:
16: alert("Strong");
17: break;
18: }
19: //set properties
20: validator.set_currentPassword("password");
21: validator.set_changeToPassword("pas2");
22: //call methods
23: if (validator.AllowPasswordChange())
24: {
25: alert("Password may be changed");
26: }
27: else
28: {
29: var violations = validator.get_passwordRuleViolations();
30: alert("Rule violations: " + violations.length);
31: for (i = 0; i < violations.length; i++)
32: {
33: alert("Rule violation " + i + " = " + violations[i]);
34: }
35: }
</script>
Na koniec chciałbym jeszcze chwile się zatrzymać na cyklu strony. Znajomość zdarzeń zachodzących po kolei może się nam przydać aby je np. przechwycić i odpowiednio zareagować :) Na szczęści cykl życia klienckiej części kodu jest bardzo podobny do tej serwerowej. Dotyczy to load, unload, i disposing. Oczywiście aby z tego skorzystać potrzebujemy naszego znanego kolegi ScriptManager ;) Aby “zarejestrować” zdarzenie musimy użyć następującej składni:
1: Sys.Application.add_load(PageLoad);
2: function PageLoad(sender)
3: {
4: //page-load code goes here
5: }
Biblioteka również pozwala nam wyrzucić nasza obsługę zdarzeń:
1: Sys.Application.remove_load(PageLoad);
Oczywiście możemy korzystać z innych zdarzeń. Dla przykładu zdarzenia z klasy PageRequestManager z przestrzeni nazw Sys.WebForms. Klasa ta służy do częściowej aktualizacji strony i asynchronicznego wywołania PostBack. Obejmuje ona następujące zdarzenia:
- initializeRequest – jeszcze przed rozpoczęciem asynchronicznego PostBack
- beginRequest – kiedy PostBack jest wysyłany na serwer
- pageLoading – podczas powrotu z serwera postback’a
- pageLoaded – kiedy content został już odtworzony po postBack
- endRequest – po zakończeniu PostBack
To tyle o czym chciałem powiedzieć. Chciałbym podkreślić, że w TK na koniec rozdziału jest np. pokazane jak stworzyć własny komponent ajaxowy, czy też stworzenie do nich pewnych zachowań. Warto to sobie doczytać w wolnej chwili. Życzę Wesołych Świat, czas trochę odpocząć ;) Do poniedziałku ;)