Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00229 007658 10469216 na godz. na dobę w sumie
ASP.NET AJAX Server Controls. Zaawansowane programowanie w nurcie .NET Framework 3.5. Microsoft .NET Development Series - książka
ASP.NET AJAX Server Controls. Zaawansowane programowanie w nurcie .NET Framework 3.5. Microsoft .NET Development Series - książka
Autor: , Liczba stron: 584
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-2089-0 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> .net - programowanie
Porównaj ceny (książka, ebook (-89%), audiobook).

Poznaj najlepsze techniki implementowania własnych kontrolek serwera frameworka ASP.NET

Kontrolki serwera pozwalają umieszczać dane dotyczące wyglądu przeglądarki i funkcjonalności serwera w spójnych obiektach wielokrotnego użytku. Można je stosować nie tylko na wielu stronach tej samej aplikacji szkieletu ASP.NET, ale także w wielu różnych aplikacjach tego frameworka. Oferuje on mnóstwo gotowych, zarówno wyjątkowo prostych, jak i złożonych kontrolek serwera. Co więcej -- z jego pomocą można również tworzyć własne kontrolki, posiadające funkcjonalności, których nie zaimplementowano w kontrolkach już istniejących. Jak wykorzystać ten potencjał ASP.NET?

Książka 'ASP.NET AJAX Server Controls. Zaawansowane programowanie w nurcie .NET Framework 3.5' zawiera szczegółowe wyjaśnienia i instrukcje, jak korzystać z frameworka ASP.NET AJAX w procesie tworzenia kontrolek serwera, obejmujących funkcjonalność frameworka AJAX. Dzięki temu podręcznikowi poznasz wewnętrzne mechanizmy i możliwości rozszerzania frameworka ASP.NET AJAX. Nauczysz się konstruować interaktywne kontrolki przy użyciu elementów zestawu narzędzi AJAX Control Toolkit oraz budować własne, niestandardowe usługi aplikacji.

Nie ograniczaj się -- twórz i dodawaj własne funkcjonalności AJAX do kontrolek serwera!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

ASP.NET AJAX Server Controls. Zaawansowane programowanie w nurcie .NET Framework 3.5. Microsoft .NET Development Series Autor: Adam Calderon, Joel Rumerman T³umaczenie: Miko³aj Szczepaniak ISBN: 978-83-246-2089-0 Tytu³ orygina³u: Advanced ASP.NET AJAX Server Controls For .NET Framework 3.5 Format: 168x237, stron: 584 Poznaj najlepsze techniki implementowania w³asnych kontrolek serwera frameworka ASP.NET • Jak skonstruowaæ niezale¿ne od przegl¹darek skrypty JavaScript? • Jak zbudowaæ w³asne, niestandardowe us³ugi aplikacji? • Jak zarz¹dzaæ relacjami komponentów z elementami modelu DOM? Kontrolki serwera pozwalaj¹ umieszczaæ dane dotycz¹ce wygl¹du przegl¹darki i funkcjonalnoœci serwera w spójnych obiektach wielokrotnego u¿ytku. Mo¿na je stosowaæ nie tylko na wielu stronach tej samej aplikacji szkieletu ASP.NET, ale tak¿e w wielu ró¿nych aplikacjach tego frameworka. Oferuje on mnóstwo gotowych, zarówno wyj¹tkowo prostych, jak i z³o¿onych kontrolek serwera. Co wiêcej — z jego pomoc¹ mo¿na równie¿ tworzyæ w³asne kontrolki, posiadaj¹ce funkcjonalnoœci, których nie zaimplementowano w kontrolkach ju¿ istniej¹cych. Jak wykorzystaæ ten potencja³ ASP.NET? Ksi¹¿ka „ASP.NET AJAX Server Controls. Zaawansowane programowanie w nurcie .NET Framework 3.5” zawiera szczegó³owe wyjaœnienia i instrukcje, jak korzystaæ z frameworka ASP.NET AJAX w procesie tworzenia kontrolek serwera, obejmuj¹cych funkcjonalnoœæ frameworka AJAX. Dziêki temu podrêcznikowi poznasz wewnêtrzne mechanizmy i mo¿liwoœci rozszerzania frameworka ASP.NET AJAX. Nauczysz siê konstruowaæ interaktywne kontrolki przy u¿yciu elementów zestawu narzêdzi AJAX Control Toolkit oraz budowaæ w³asne, niestandardowe us³ugi aplikacji. • Programowanie w jêzyku JavaScript • Obs³uga b³êdów • £añcuchy, zmienne i argumenty funkcji • Programowanie biblioteki Microsoft AJAX Library • Dziedziczenie i implementacja interfejsu • Typy wyliczeniowe • Kontrolki • Obiekt Sys. Application • Dodawanie funkcji klienckich do kontrolek serwera • Lokalizacja we frameworku ASP.NET AJAX • Wytwarzanie kontrolek w œrodowisku czêœciowej komunikacji zwrotnej • Us³ugi aplikacji • Architektura strony klienckiej i architektura serwera Nie ograniczaj siê — twórz i dodawaj w³asne funkcjonalnoœci AJAX do kontrolek serwera! Spis treści I 1 Słowo wstępne Przedmowa Podziękowania O autorach KOD KLIENTA 13 15 23 27 Wprowadzenie do JavaScriptu Atrybuty języka JavaScript Proste typy danych Programowanie w języku JavaScript Ogólnie o języku JavaScript 31 32 32 32 34 35 Łańcuchy 36 Obiekty 43 Zmienne i argumenty funkcji Obsługa błędów 51 Opóźnianie wykonywania kodu za pomocą limitów i przedziałów czasowych 56 64 65 71 75 Programowanie obiektowe w języku JavaScript Abstrakcyjne typy danych Dziedziczenie Podsumowanie 2 Programowanie biblioteki Microsoft AJAX Library Rozszerzanie wbudowanych typów języka JavaScript Wartości logiczne Daty i liczby 77 78 78 79 6 Spis treści Łańcuchy Tablice Rozszerzanie biblioteki Microsoft AJAX Library Klasy Interfejsy Typy wyliczeniowe Dziedziczenie i implementacja interfejsu Ważne nowe typy Typ Sys.EventHandlerList Typ Sys.StringBuilder Obiekt Sys.Debug Typ Sys.UI.DomElement Typ Sys.UI.DomEvent Zarządzanie zasięgiem Delegacje Wywołania zwrotne Podsumowanie II 3 KONTROLKI Komponenty Definicja komponentów Komponenty, kontrolki i zachowania Typ Sys.Component Definiowanie nowych komponentów Tworzenie komponentów Podsumowanie wiedzy o komponentach Kontrolki Nowe pojęcia Definiowanie nowej kontrolki Tworzenie kontrolki Podsumowanie wiedzy o kontrolkach Zachowania Definiowanie zachowania Tworzenie zachowania Podsumowanie wiedzy o zachowaniach Podsumowanie 79 80 85 85 92 96 101 111 111 117 118 123 128 133 134 135 137 141 141 142 144 148 153 168 168 170 172 174 175 175 177 178 183 183 4 Obiekt Sys.Application Informacje podstawowe Tworzenie obiektu Sys.Application Informacje o typie Informacje o metodach Menedżer komponentów Dodawanie komponentu Odnajdywanie komponentu Usuwanie komponentu Uzyskiwanie komponentów Procedura inicjalizacji Proces tworzenia komponentów Zdarzenie load Procedura zwalniania Metoda Sys.Application.dispose Podsumowanie Spis treści 7 185 185 185 187 188 190 191 194 197 198 198 202 211 215 216 218 5 Dodawanie funkcji klienckich do kontrolek serwera Architektura generowania skryptów Generowanie skryptów zachowań i kontrolek Zasoby skryptu Kontrolka ScriptManager 219 220 220 225 228 230 231 231 Dodawanie funkcjonalności klienta z wykorzystaniem klasy ScriptControl 242 243 245 Wprowadzenie do klasy ExtenderControl Tworzenie kontrolki rozszerzającej Dodawanie zachowania klienta z wykorzystaniem klasy ExtenderControl Przegląd klasy ScriptControl Tworzenie kontrolki skryptu Dodawanie funkcjonalności klienta do kontrolek kompozytowych z wykorzystaniem interfejsu IScriptControl Przegląd kontrolek kompozytowych Interfejs IScriptControl Tworzenie kontrolki kompozytowej Podsumowanie 254 254 256 257 261 8 Spis treści 6 Lokalizacja we frameworku ASP.NET AJAX Lokalizacja we frameworku ASP.NET Określenie, które elementy wymagają lokalizacji Przystosowanie aplikacji do reguł określonej kultury Lokalizacja wyświetlanych wartości Lokalizacja we frameworku ASP.NET AJAX Mechanizmy lokalizacyjne języka JavaScript Mechanizmy lokalizacyjne ASP.NET AJAX Podsumowanie 7 Wytwarzanie kontrolek w środowisku częściowej komunikacji zwrotnej Działanie kontrolki UpdatePanel Wpływ częściowej komunikacji zwrotnej na komponenty klienckie Automatyczne zwalnianie zachowań i kontrolek Automatyczne zwalnianie komponentów Ręczne zwalnianie komponentów, kontrolek lub zachowań Ładowanie wyrażeń i plików języka JavaScript Metody rejestrowania skryptów w kontrolce ScriptManager Metoda Sys.Application.notifyScriptLoaded() Zdarzenia obiektu Sys.Application III 8 Zdarzenie init Zdarzenie load Podsumowanie KOMUNIKACJA Architektura komunikacji frameworku ASP.NET AJAX Nowy model komunikacji Architektura komunikacji frameworku ASP.NET AJAX 2.0 Extensions Usługi sieciowe Metody stron Serializacja Komponenty frameworku stosowane po stronie serwera Architektura komunikacji biblioteki Microsoft AJAX Library Usługi pośredniczące Serializacja 263 263 265 269 274 283 284 287 320 321 322 327 332 340 343 357 357 363 365 365 366 368 371 372 374 375 385 386 391 398 398 411 Spis treści 9 Klasa WebRequest Komponenty rdzenia żądania sieciowego Podsumowanie 9 Usługi aplikacji Usługi członkostwa, ról i profilów użytkowników frameworku ASP.NET 2.0 Uwierzytelnianie formularzy ASP.NET 2.0 Provider Model Narzędzie Web Site Administration Tool Członkostwo Role Profile Usługi aplikacji frameworku ASP.NET AJAX Usługa uwierzytelniania Usługa ról Usługa profilów Niestandardowe usługi aplikacji Klasa ServiceHandlerFactory protokołu HTTP i klasy pomocnicze Pośrednik w dostępie do usługi Konfiguracja Podsumowanie IV ZESTAW NARZĘDZI AJAX CONTROL TOOLKIT 10 Architektura zestawu narzędzi ASP.NET AJAX Control Toolkit Przegląd zestawu narzędzi ASP.NET AJAX Control Toolkit Atrybuty jako sposób na uproszczenie programowania Bogaty zbiór klas platformy .NET Bogaty zbiór klas języka JavaScript Obsługa animacji Struktura zestawu narzędzi ASP.NET AJAX Control Toolkit Instalacja Układ pliku rozwiązania Architektura serwera Atrybuty Klasy bazowe dla kontrolek rozszerzających i kontrolek skryptowych Klasy projektowe 412 417 419 421 421 422 425 427 427 432 438 441 441 446 448 451 454 463 467 468 471 471 472 472 473 473 473 473 474 475 476 480 485 10 Spis treści Architektura strony klienckiej Klasa BehaviorBase Klasa ControlBase Animacje Struktura i rodzaje animacji Architektura klienta Architektura serwera Podsumowanie 488 489 490 490 490 492 496 500 11 Dodawanie funkcji klienckich do kontrolek serwera z wykorzystaniem zestawu narzędzi ASP.NET AJAX Control Toolkit 501 Dodawanie zachowań strony klienckiej za pomocą klasy ExtenderControlBase Szablon biblioteki kontrolki rozszerzającej środowiska Visual Studio 2008 Dziedziczenie po klasie bazowej ExtenderControlBase Tworzenie klasy AjaxControlToolkit.BehaviorBase Dołączanie kontrolki rozszerzającej do kontrolki docelowej Wnioski końcowe Dodanie mechanizmów obsługi trybu projektowania do naszej kontrolki rozszerzającej Domyślne funkcje trybu projektowania Dodawanie klas projektujących i edytorów do właściwości Dodawanie animacji do naszej kontrolki rozszerzającej Animacje tworzone z wykorzystaniem interfejsu API JavaScriptu Animacje tworzone z wykorzystaniem modelu deklaratywnego Podsumowanie DODATKI A JavaScript w środowisku Visual Studio Technologia IntelliSense Odwołania do bibliotek i usług sieciowych Komentarze języka XML B Weryfikacja parametrów metod 501 502 506 508 510 511 511 511 512 518 518 522 528 531 531 531 535 539 Spis treści 11 C D Obiekty obsługujące i moduły frameworku ASP.NET Cykl życia aplikacji ASP.NET Obiekty obsługujące żądania protokołu HTTP Przegląd obiektów obsługujących protokołu HTTP Przegląd fabryki obiektów obsługujących protokołu HTTP Moduły protokołu HTTP Przegląd modułów protokołu HTTP Kod obsługujący błędy klientów Klasa kliencka ErrorHandler Klasa kliencka ErrorEventArgs ErrorHandler Server Control Klasa kliencka StackTrace Usługa sieciowa ErrorDataService Strona testująca mechanizm obsługi błędów Skorowidz 543 543 544 544 546 548 548 553 553 555 555 556 557 558 559 3 Komponenty W ROZDZIALE 2., ZATYTUŁOWANYM „Programowanie biblioteki Microsoft AJAX Library” rozpoczęliśmy omawianie biblioteki Microsoft AJAX Library i sposobów, w jakie jej twórcy poszerzyli wbudowane typy JavaScriptu o nowe funkcje, technik wyko- rzystywania modelu prototypowego do rozszerzania samej biblioteki Microsoft AJAX Library o nasze typy niestandardowe, a nawet kilku ważnych aspektów działania typów wbudowanych. W tym rozdziale będziemy kontynuować omawianie biblioteki Microsoft AJAX Library — tym razem jednak skoncentrujemy się na komponentach i ich typach potomnych: kon- trolkach i zachowaniach. W niniejszym rozdziale przystąpimy też do tworzenia obiektów klienckich związanych z kontrolkami serwera. Definicja komponentów Komponentem jest każdy obiekt, którego typ kliencki dziedziczy po typie Sys.Component. Wspomniany typ bazowy Sys.Component jest bardzo ważny, ponieważ właśnie za jego pomocą będziemy rozszerzać ten framework o nowe komponenty. Tworzenie nowych komponentów z wykorzystaniem tego typu jest o tyle uzasadnione, że właśnie typ Sys.Component ma kilka specyficznych cech, których nie oferuje żaden inny typ biblioteki Microsoft AJAX Library. Po pierwsze, komponenty są projektowane z myślą o zapełnianiu luki dzielącej oprogra- mowanie klientów i serwerów. Za pośrednictwem obiektów serwera nazywanych deskrypto- rami skryptów (ang. script descriptors) możemy wymuszać na frameworku ASP.NET AJAX automatyczne generowanie kodu JavaScriptu odpowiedzialnego za tworzenie eg- zemplarzy naszych typów komponentów. W ten sposób możemy wiązać mechanizmy oprogramowania klienckiego z kontrolkami naszego serwera WWW bez konieczności umieszczania jakichkolwiek skryptów języka JavaScript w klasach tych kontrolek. 142 Rozdział 3. Komponenty UWAGA Tworzenie komponentów za pośrednictwem kodu serwera Techniki tworzenia komponentów za pośrednictwem kodu serwera zostaną szczegółowo omówione w rozdziale 5., zatytułowanym „Dodawanie funkcji klienckich do kontrolek serwera”. Po drugie, Sys.Application, czyli obiekt globalny występujący w roli swoistego śro- dowiska wykonawczego klienta, jest tworzony w sposób umożliwiający skuteczne zarzą- dzanie wszystkimi typami dziedziczącymi po typie Sys.Component. Oznacza to, że cykl życia naszych komponentów ma charakter predefiniowany. Nie tylko będziemy na bieżąco informowani o tworzeniu i niszczeniu tych komponentów, ale też będziemy mogli — w razie konieczności — wykonywać własny, niestandardowy kod przy okazji każdego z tych zdarzeń. Takie rozwiązanie gwarantuje nam pełną kontrolę i wysoki poziom bez- pieczeństwa. UWAGA Komponenty i kontrolki serwera WWW Sposób zarządzania komponentami przez obiekt Sys.Application przypomina tech- niki zarządzania kontrolkami serwera WWW przez stronę. Ta zbieżność nie jest przypadkowa — zaprojektowano ten mechanizm w taki sposób, aby biblioteka Microsoft AJAX Library tworzyła możliwie przyjazne środowisko dla programistów ASP.NET. Obiekt Sys.Application zostanie szczegółowo omówiony w rozdziale 4., zatytułowanym „Obiekt Sys.Application”. I wreszcie komponenty oferują wiele wbudowanych popularnych elementów funkcjo- nalności, których będziesz potrzebował podczas tworzenia swoich aplikacji. Dysponują na przykład egzemplarzem typu Sys.EventHandlerList, dzięki któremu możesz tworzyć, obsługiwać i generowania niestandardowe zdarzenia. Komponenty implementują też in- terfejs Sys.INotifyPropertyChanged z metodami powiadomień o zmianach właściwości. Co więcej, komponenty implementują interfejs Sys.INotifyDisposing, dzięki czemu pozostałe obiekty można łatwo powiadamiać o zwalnianiu egzemplarzy komponentów. Komponenty, kontrolki i zachowania Jakby tego było mało (jakby te komponenty nie wystarczyły), biblioteka Microsoft AJAX Library dodatkowo oferuje dwa wyspecjalizowane typy komponentów: zachowania (ang. behaviors), reprezentowane przez klasę Sys.UI.Behavior, i kontrolki (ang. controls), reprezentowane przez klasę Sys.UI.Control. Hierarchię łączącą te trzy typy przedstawiono na rysunku 3.1. Definicja komponentów 143 RYSUNEK 3.1. Hierarchia klas łącząca typy Sys.Component, Sys.UI.Behavior i Sys.UI.Control WSKAZÓWKA Komponenty zarządzane Jak widać na rysunku 3.1, Sys.Component jest typem bazowym zarówno dla typu Sys.UI.Control, jak i dla typu Sys.UI.Behavior. We wcześniejszej części tego rozdziału wspomniano już, że za zarządzanie komponentami odpowiada obiekt Sys.Application — dzięki opisywanej relacji dziedziczenia ten sam obiekt zarządza także kontrolkami i zachowaniami. Kiedy mówimy o obiekcie Sys.Application, mamy na myśli struk- turę zarządzającą komponentami, czyli zachowaniami, kontrolkami lub komponen- tami jako takimi. W praktyce zachowania, kontrolki i komponenty są niemal identyczne. Ich podobień- stwo wynika z tego, że zachowania i kontrolki dziedziczą zdecydowaną większość swojej funkcjonalności po typie bazowym, czyli po klasie komponentów, nie dodając wiele od siebie. Co więcej, funkcjonalność implementowana przez te dwa typy potomne nie wpro- wadza żadnych istotnych zmian. Jedną z najważniejszych różnic dzielących klasę bazową komponentów od zachowań i kontrolek jest wbudowany w te dwa typy potomne związek z elementem modelu DOM. Twórcy tych klas zdecydowali się na dołączenie tego związku, ponieważ zachowania i kontrolki w założeniu mają mieć charakter wizualny. Z drugiej strony komponenty nie dysponują wbudowanymi związkami z elementami DOM, które nie muszą mieć postaci konstrukcji wizualnych. Najważniejszą różnicą dzielącą zachowania i kontrolki jest to, że z pojedynczym ele- mentem modelu DOM można związać tylko jedną kontrolkę; ale wiele zachowań. W tabeli 3.1 podsumowano różnice dzielące wszystkie trzy typy. Opisane w tabeli reguły są wymuszane w trakcie tworzenia komponentów, zachowań i kontrolek. Właśnie na podstawie tych cech powinniśmy podejmować decyzje o wyborze właściwego typu bazowego dla naszych nowych typów potomnych. Prosty diagram ilu- strujący proces oceny tych właściwości podczas wyboru typu, po którym powinien dzie- dziczyć nowy typ potomny, pokazano na rysunku 3.2. 144 Rozdział 3. Komponenty TABELA 3.1. Różnice pomiędzy komponentami, kontrolkami i zachowaniami Typ obiektu Możliwość związków z elementem DOM Komponent Brak możliwości Kontrolka Musi być związany z jakimś elementem modelu DOM. Możliwość związków większej liczby tego rodzaju obiektów z elementem DOM Brak możliwości (komponent nie może być bezpośrednio związany z elementem DOM) Nie, pojedynczy element DOM może być związany z tylko jedną kontrolką. Zachowanie Musi być związany z jakimś elementem modelu DOM. Tak, pojedynczy element DOM może być związany z jedną lub wieloma zachowaniami. Dostępność obiektu z poziomu związanego elementu DOM Brak możliwości (komponent nie może być bezpośrednio związany z elementem DOM) Tak, dostęp do kontrolki można uzyskiwać za pośrednictwem właściwości expando nazwanej control i dołączonej do elementu DOM. Tak, dostęp do zachowania jest możliwy za pośrednictwem właściwości expando nazwanej tak jak to zachowanie (pod warunkiem że to zachowanie było nazwane w czasie inicjalizacji elementu DOM). Wszystkie zachowania związane z elementem DOM są dostępne za pośrednictwem prywatnej tablicy _behaviors dołączonej do tego elementu. Skoro opanowaliśmy już podstawy komponentów, kontrolek i zachowań, możemy przystąpić do szczegółowego omówienia każdego z tych typów. Typ Sys.Component Sys.Component jest typem bazowym dla wszystkich komponentów i jako taki oferuje zdecydo- waną większość ich elementów funkcjonalności. Typ Sys.Component nie dziedziczy po żadnym innym typie, ale implementuje trzy interfejsy: Sys.IDisposable, Sys.INotifyPropertyChanged oraz Sys.INotifyDisposing. Wszystkie te interfejsy krótko opisano w tabeli 3.2. Typ Sys.Component 145 RYSUNEK 3.2. Proces decyzyjny dla wyboru komponentu, kontrolki lub zachowania TABELA 3.2. Interfejsy implementowane przez typ Sys.Component Interfejs Sys.INotifyPropertyChanged Sys.INotifyDisposing Sys.IDisposable Zadanie Implementuje mechanizm powiadomień o zmianach wartości właściwości. Implementuje zdarzenie zwalniania komponentu. Reprezentuje obiekt przeznaczony do zwolnienia. Metody add_propertyChanged remove_propertyChanged add_disposing remove_disposing dispose Klasa Sys.Component zawiera też pięć wewnętrznych składowych, które opisano w tabeli 3.3. 146 Rozdział 3. Komponenty TABELA 3.3. Składowe typu Sys.Component Nazwa składowej _id Zadanie Reprezentuje unikatowy identyfikator danego komponentu. Składowa _id jest wykorzystywana do odnajdywania komponentów zarejestrowanych w obiekcie Sys.Application. Każdy komponent zarządzany przez ten obiekt musi mieć unikatowy identyfikator. Określa, czy prawidłowo ustawiono właściwość _id. _idSet _initializing Określa, czy dany komponent jest w trakcie procesu _updating _events inicjalizacji. Określa, czy dany komponent jest aktualizowany. Reprezentuje listę zdarzeń i metod obsługujących. Typ string boolean boolean boolean Sys.Event ´HandlerList Oprócz implementowania metod wymaganych przez trzy interfejsy wymienione w ta- beli 3.2, klasa Sys.Component udostępnia kilka metod dodatkowych, które umożliwiają nam operowanie na jej wewnętrznych składowych. W tabeli 3.4 szczegółowo opisano za- równo te metody dodatkowe, jak i metody wymagane przez wspomniane trzy interfejsy. TABELA 3.4. Metody klasy Sys.Component Nazwa metody Składnia beginUpdate endUpdate updated get_isUpdating initialize get_initialized comp.endUpdate(); comp.beginUpdate(); Opis Oznacza dany komponent jako aktualizowany. Metoda beginUpdate jest wywoływana w czasie tworzenia komponentu. Oznacza dany komponent jako nieaktualizowany. Metoda endUpdate, która jest wywoływana w czasie tworzenia komponentu, wykonuje metody initialize (jeśli dany komponent nie jest inicjalizowany) i updated. Implementacja pusta. Zwraca wartość składowej _updating. Oznacza dany komponent jako inicjalizowany. Zwraca wartość składowej _initialized. comp.get_initialized(); comp.get_isUpdating(); comp.initialize(); comp.updated(); Typ Sys.Component 147 Składnia comp.dispose(); comp.get_events(); comp.get_id(); comp.set_id(id); comp.add_disposing (handler); comp.remove_disposing (handler); comp.add_propertyChanged (handler); comp.remove_property ´Changed (handler); comp.raisePropertyChanged (propertyName); TABELA 3.4. Metody klasy Sys.Component — ciąg dalszy Nazwa metody dispose get_events get_id set_id add_disposing Opis Wykonuje metody obsługujące zdarzenie disposing. Usuwa właściwość _events z danego komponentu i anuluje jego rejestrację w obiekcie Sys.Application. Zwraca wartość składowej _events. Zwraca wartość składowej _id. Ustawia wartość składowej _id. Raz ustawionego identyfikatora nie można zmieniać (za pośrednictwem tej metody); identyfikator nie może być zmieniany także po rejestracji komponentu w obiekcie Sys.Application. Dodaje metodę obsługującą zdarzenie disposing. remove_disposing Usuwa metodę obsługującą add_propertyChan ged remove_propertyC hanged raisePropertyCha nged zdarzenie disposing. Dodaje metodę obsługującą zdarzenie propertyChanged. Usuwa metodę obsługującą zdarzenie propertyChanged. Wykonuje zarejestrowane metody obsługujące zdarzenie propertyChanged, przekazując na ich wejściu (w formie argumentów zdarzenia) nazwę zmodyfikowanej właściwości. WSKAZÓWKA Metody beginUpdate, endUpdate oraz initialize Metody beginUpdate, endUpdate oraz initialize są wykonywane automatycznie w ramach procesu tworzenia komponentu. Wymienione metody z reguły nie są wy- woływane z poziomu kodu zdefiniowanego przez użytkownika, ale można je przy- kryć, aby umieścić w nich jakąś niestandardową funkcjonalność. 148 Rozdział 3. Komponenty Definiowanie nowych komponentów Klasa Sys.Component jest niezwykle przydatna, jednak jej celem nie jest bezpośrednie tworzenie egzemplarzy tego typu. Przeciwnie, twórcy klasy Sys.Component chcieli opra- cować typ, który będzie wykorzystywany w roli klasy bazowej dla komponentów definio- wanych przez użytkowników. Nowy typ komponentu możemy zdefiniować, stosując model prototypowy omówiony w rozdziale 2. i rejestrując nasz komponent jako typ dziedziczący po klasie bazowej Sys.Component. Komponent ErrorHandler Aby zademonstrować technikę definiowania nowych komponentów, utworzymy nowy, własny komponent obsługi błędów. Komponent ErrorHandler będzie odpowiadał za pu- blikowanie obsługiwanych i nieobsługiwanych błędów z wykorzystaniem specjalnej usługi danych o błędach. Szkielet Na początek utworzymy szkielet naszego nowego komponentu (patrz listing 3.1). LISTING 3.1. Definiowanie komponentu ErrorHandler /// reference name= MicrosoftAjax.js / ErrorHandler = function() { ErrorHandler.initializeBase(this); }; ErrorHandler.prototype = { initialize: function() { ErrorHandler.callBaseMethod(this, initialize ); }, dispose: function() { ErrorHandler.callBaseMethod(this, dispose ); } } ErrorHandler.registerClass( ErrorHandler , Sys.Component); Oprócz wywołania metody initializeBase w konstruktorze naszego typu i rejestracji tej klasy jako typu dziedziczącego po klasie Sys.Component zdecydowaliśmy się na przy- krycie metod initialize i dispose klasy bazowej Sys.Component. Umieściliśmy te przy- kryte metody w szkielecie naszego komponentu, ponieważ właśnie przykrywanie initialize i dispose jest zwykle jednym z pierwszych kroków procesu tworzenia komponentu (podobną kolejność sugerujemy także Tobie). Typ Sys.Component 149 Inicjalizacja i niszczenie komponentu Naszą szkieletową definicję należy jeszcze uzupełnić o implementacje metod initialize i dispose. W metodzie initialize konstruujemy nasz komponent. Proces konstruowania kom- ponentu obejmuje takie kroki jak dodanie metod obsługujących zdarzenia do elementów modelu DOM, dołączenie nowego elementu DOM do drzewa i wszystkie inne operacje wymagane przez konkretny typ komponentów. W metodzie dispose powinniśmy umieścić kod odpowiedzialny za zwalnianie naszego komponentu. Zwalnianie komponentu może obejmować odłączenie zdarzeń od elementu modelu DOM, zniszczenie ewentualnych utworzonych elementów DOM lub zwolnienie wszelkich innych zasobów utworzonych przez nasz komponent. WSKAZÓWKA Metodę dispose można wywołać więcej niż raz Warto tak implementować metodę dispose, aby można ją było wywoływać więcej niż raz bez ryzyka powodowania błędów czasu wykonywania. W przypadku dość złożonych aplikacji nietrudno o sytuację, w której zwalniany obiekt menedżera wywoła metodę dispose wszystkich swoich komponentów potomnych. Okazuje się jednak, że każdy z tych obiektów potomnych jest zarejestrowany także w obiekcie Sys.Application jako obiekt, który może być zwalniany (który implementuje interfejs Sys.IDisposable). Zwalniany obiekt Sys.Application automatycznie wykonuje metodę dispose każ- dego z tak zarejestrowanych obiektów — oznacza to, że metody dispose tych obiek- tów są wykonywane (co najmniej) dwukrotnie. Jeśli nie zachowamy należytej ostroż- ności, próby wielokrotnego wykonania metody dispose będą się kończyły błędami czasu wykonywania. Można jednak uniknąć tych problemów, stosując proste mecha- nizmy weryfikacji w formie wyrażeń warunkowych. Na potrzeby naszego nowego komponentu ErrorHandler należy dodać metodę obsłu- gującą zdarzenie error obiektu window przy okazji inicjalizacji tego komponentu i usunąć tę metodę przy okazji jego zwalniania. Proponowaną implementację tego mechanizmu przedstawiono na listingu 3.2. LISTING 3.2. Dodawanie metody obsługującej zdarzenie error obiektu okna /// reference name= MicrosoftAjax.js / ErrorHandler = function () { ErrorHandler.initializeBase(this); }; ErrorHandler.prototype = { initialize: function () { ErrorHandler.callBaseMethod(this, initialize ); window.onerror = Function.createDelegate(this, this._unhandledError); 150 Rozdział 3. Komponenty }, dispose: function ErrorHandler$dispose() { window.onerror = null; ErrorHandler.callBaseMethod(this, dispose ); }, _unhandledError: function(msg, url, lineNumber) { try { var stackTrace = StackTrace.createStackTrace(arguments.callee); ErrorDataService.PublishError (stackTrace, msg, url, lineNumber); } catch (e) { } } } ErrorHandler.registerClass( ErrorHandler , Sys.Component); Jak widać na listingu 3.2, wykorzystaliśmy w naszym kodzie kilka interesujących zabie- gów. Po pierwsze, w metodzie initialize utworzono delegację wskazującą na metodę _unhandledError i skojarzono tę delegację ze zdarzeniem error obiektu window (przypi- sując ją zdarzeniu onerror). WSKAZÓWKA Zdarzenie window.onerror W omawianym kodzie wykorzystano konstrukcję polegającą na przypisaniu delegacji zdarzeniu onerror (zamiast metody $addHandler), ponieważ z jakiegoś powodu zda- rzenie error obiektu okna nie obsługuje dodawania zdarzeń za pomocą metod ad- dEventListener ani attachEvent (czyli dwóch metod właściwych dla konkretnych przeglądarek i ostatecznie wywoływanych przez metodę $addHandler). W metodzie dispose usunęliśmy metodę obsługującą zdarzenie error obiektu window. Dysponujemy więc gotowym kodem inicjalizującym i zwalniającym nasz komponent. W metodzie unhandledError, która będzie wykonywana w odpowiedzi na wystąpienia nieobsługiwanych błędów, podejmujemy dwa działania. Po pierwsze, generujemy ślad stosu, korzystając z globalnego obiektu StackTrace przekazywanego za pośrednictwem właściwości callee zmiennej arguments naszej funkcji. Po uzyskaniu obiektu śladu stosu wykonujemy metodę PublishError pośrednika naszej usługi sieciowej ErrorDataService (za pośrednictwem tej metody przekazujemy na serwer ślad stosu, komunikat o błędzie, adres URL strony, na której ten błąd wystąpił, i numer wiersza kodu). Cały ten kod dodat- kowo umieściliśmy w ramach wyrażenia try-catch, ponieważ nie chcemy, aby kod obsłu- gujący błędy sam generował jakiekolwiek błędy czasu wykonywania. Typ Sys.Component 151 WSKAZÓWKA Obiekty StackTrace i ErrorDataService Globalny obiekt StackTrace, który wykorzystano do wygenerowania naszego śladu stosu wykonywania, jest niezwykle przydatny w procesie diagnozowania kodu, a jego kompletny kod źródłowy przedstawiono w dodatku D, zatytułowanym „Kod obsłu- gujący błędy klientów”. Podobnie, kod usługi sieciowej ErrorDataService, której użyto do odesłania informacji o błędzie na serwer, także można znaleźć we wspomnia- nym dodatku. Stosowanie metod i obiektów klasy bazowej Skoro nasz typ dziedziczy po klasie Sys.Component, z natury rzeczy przejmuje wszystkie atrybuty i zachowania tego typu bazowego. Korzystając z obiektu Sys.EventHandlerList dostępnego w ramach tej klasy (i jego funkcjonalności), możemy definiować nowe zdarze- nia bez konieczności samodzielnego pisania sporej ilości kodu. Na listingu 3.3 rozszerzono nasz komponent ErrorHandler o zdarzenie, które możemy zarejestrować jako generowane w odpowiedzi na wystąpienia błędów. LISTING 3.3. Korzystanie z metod klasy bazowej … // wcześniejszy kod komponentu pozostaje niezmieniony _unhandledError: function (msg, url, lineNumber) { try { var stackTrace = StackTrace.createStackTrace(arguments.callee); ErrorDataService.PublishError (stackTrace, msg, url, lineNumber); var args = new ErrorEventArgs(stackTrace, msg, url, lineNumber); this._raiseUnhandledErrorOccured(args); } catch (e) { } }, add_unhandledErrorOccurred: function(handler) { this.get_events().addHandler( unhandledErrorOccurred , handler); }, remove_unhandledErrorOccurred: function(handler) { this.get_events().removeHandler( unhandledErrorOccurred , handler); }, _raiseUnhandledErrorOccured: function(args) { var evt = this.get_events().getHandler( unhandledErrorOccurred ); if (evt !== null) { evt(this, args); 152 Rozdział 3. Komponenty } }, } ErrorHandler.registerClass( ErrorHandler , Sys.Component) ; ErrorEventArgs = function(stackTrace, message, url, lineNumber) { ErrorEventArgs.initializeBase(this); this._message = message; this._stackTrace = stackTrace; this._url = url; this._lineNumber = lineNumber; } ErrorEventArgs.registerClass( ErrorEventArgs , Sys.EventArgs); Skoncentrujmy się najpierw na końcowej części listingu 3.3, gdzie zdefiniowano nowy typ ErrorEventArgs. Typ ErrorEventArgs dziedziczy po typie Sys.EventArgs i prze- kształca nasze informacje o błędzie w obiekt. W typie ErrorHandler dodano trzy metody niezbędne do dodawania, usuwania i ge- nerowania zdarzenia unhandledErrorOccurred. W naszym kodzie wykorzystujemy listę metod obsługujących utrzymywaną przez obiekt Sys.Component — dostęp do listy samych zdarzeń uzyskujemy za pośrednictwem metody this.get_events(). I wreszcie w metodzie _unhandledError dodaliśmy kod tworzący argumenty zdarzenia błędu i przekazujący je do metody, która to zdarzenie generuje. Ostatnia zmiana, którą musimy wprowadzić do komponentu ErrorHandler, polega na dodaniu właściwości umożliwiającej włączanie i wyłączanie trybu publikowania błędów. Niezbędne modyfikacje przedstawiono na listingu 3.4. LISTING 3.4. Dodanie właściwości wyłączającej tryb publikacji błędów /// reference name= MicrosoftAjax.js / ErrorHandler = function () { ErrorHandler.initializeBase(this); this._disableErrorPublication = false; }; ErrorHandler.prototype = { … get_disableErrorPublication: function() { return this._disableErrorPublication; }, set_disableErrorPublication: function(value) { if (!this.get_updating()) { this.raisePropertyChanged( disableErrorPublication ); } this._disableErrorPublication = value; }, Typ Sys.Component 153 _unhandledError: function(msg, url, lineNumber) { try { var stackTrace = StackTrace.createStackTrace(arguments.callee); if (!this._disableErrorPublication) { ErrorDataService.PublishError (stackTrace, msg, url, lineNumber); } var args = new ErrorEventArgs(stackTrace, msg, url, lineNumber); this._raiseUnhandledErrorOccured(args); } catch (e) { } }, … } ErrorHandler.registerClass( ErrorHandler , Sys.Component); … Ostatnia zmiana czyni z naszego typu przydatny komponent, który można z powodze- niem wykorzystywać do wysyłania na serwer informacji o błędach występujących po stro- nie klienta i — tym samym — powiadamiania programistów o ewentualnych problemach. Tworzenie komponentów Na podstawie materiału zaprezentowanego w tym rozdziale do tego momentu można by pomyśleć, że tworzenie nowych komponentów polega na konstruowaniu ich obiektów i przypisywaniu ich odpowiednim zmiennym (patrz listing 3.5). LISTING 3.5. Tworzenie egzemplarza komponentu za pomocą słowa new var errorHandler = new ErrorHandler(); errorHandler.set_disableErrorPublication (false); Chociaż w kodzie w tej formie nie ma niczego nieprawidłowego (w końcu egzemplarz komponentu jest właśnie obiektem JavaScriptu), komponenty należy tworzyć raczej za pośrednictwem metody Sys.Component.create. Składnię wywołań metody create przed- stawiono na listingu 3.6. LISTING 3.6. Tworzenie egzemplarza komponentu za pośrednictwem metody Sys.Component.create var newComponent = Sys.Component.create( type, properties, events, references, element); 154 Rozdział 3. Komponenty Działanie metody Sys.Component.create, która jest dostępna także za pośrednictwem zmiennej globalnej $create, nie ogranicza się tylko do tworzenia nowego egzemplarza określonego typu. Oprócz tworzenia nowego egzemplarza wskazanego typu metoda Sys.Component.create rejestruje ten egzemplarz w obiekcie Sys.Application jako kom- ponent zarządzany, po czym automatycznie wywołuje metody beginUpdate, endUpdate, updating i initialize tego komponentu. Wszystkie te działania są podejmowane auto- matycznie w zależności od parametrów użytych w jej wywołaniu. Co więcej, metoda $create może przypisywać wartości początkowe właściwości, dodawać metody obsługujące zda- rzenia, przypisywać inne komponenty w formie referencji i wiązać z danym komponen- tem element modelu DOM. I wreszcie metoda $create zwraca wskaźnik do nowo utwo- rzonego egzemplarza. Jak widać, metoda $create robi dużo więcej niż tylko tworzenie nowego egzemplarza naszego typu. UWAGA Wykonywanie metody initialize Metoda initialize zawsze jest wykonywana po ustawieniu wszystkich właściwości, zdarzeń i referencji. Co ważne, metoda $create tworzy nie tylko egzemplarze typów, które bezpośrednio dziedziczą po klasie Sys.Component, ale też egzemplarze typów na wielu poziomach dzie- dziczenia pomiędzy wskazanym typem a typem Sys.Component. Zbiór tych egzemplarzy obejmuje kontrolki dziedziczące po typie Sys.UI.Control i zachowania dziedziczące po typie Sys.UI.Behavior. Metoda $create działa inaczej w przypadku zachowań i inaczej w przypadku kontrolek — te nieznaczne różnice zostaną omówione przy okazji analizy tych typów w dalszej części tego rozdziału. UWAGA Parametry metody $create Jedynym parametrem wymaganym przez metodę $create jest type. Pozostałe para- metry — properties, events, references i element — mają opcjonalny charakter. Jeśli nie chcemy z nich korzystać, powinniśmy w ich miejsce przekazać wartość null. Przekazanie wartości innej niż null za pośrednictwem parametru element jest pra- widłowe, pod warunkiem że typ, którego egzemplarz tworzymy, dziedziczy po klasie Sys.UI.Control lub Sys.UI.Behavior. Jeśli przekażemy ten parametr w sytuacji, gdy tworzony egzemplarz komponentu nie dziedziczy po żadnym z wymienionych typów, zostanie wygenerowany błąd. Podobnie, jeśli nie przekażemy wartości innej niż null za pośrednictwem parametru element podczas tworzenia egzemplarza typu dziedziczącego po klasie Sys.UI.Control lub Sys.UI.Behavior, zostanie wygenerowany błąd. Typ Sys.Component 155 Aby zademonstrować sposób użycia metody $create, przeanalizujemy szereg jej wy- wołań, zmieniając parametry przekazywane na wejściu tej metody. UWAGA Obiekt Sys.Application jest zainicjalizowany W poniższym materiale poświęconym działaniu metody $create zakładamy, że obiekt Sys.Application został już zainicjalizowany. Mimo że komponenty nie zaw- sze są tworzone w takich okolicznościach, decydujemy się na to założenie na potrzeby początkowej analizy metody $create, aby uprościć nasze dalsze rozważania. Okazuje się jednak, że istnieje kilka istotnych różnic dzielących proces tworzenia komponentów po inicjalizacji obiektu Sys.Application od procesu tworzenia kom- ponentów przed pełną inicjalizacją tego obiektu — zmiany działania metody $create wskutek niepełnej inicjalizacji obiektu Sys.Application zostaną omówione przy okazji analizy samego procesu inicjalizacji tego obiektu w rozdziale 4. Stosowanie parametru type Przyjrzyjmy się najpierw podstawowemu wywołaniu metody $create polegającemu na przekazaniu samego typu obiektu, który chcemy utworzyć, oraz wartości null w miejsce pozostałych parametrów. Przykład takiego wywołania przedstawiono na listingu 3.7. LISTING 3.7. Parametr type var errorHandler = Sys.Component.create( ErrorHandler, null, null, null, null); type Opis: Typ tworzonego komponentu Oczekiwany typ: type Wymagany: Tak Pozostałe wymagania: Obiekt przekazany za pośrednictwem tego parametru musi być egzemplarzem typu dziedziczącego po klasie Sys.Component. Uwagi: Parametr nie jest otoczony cudzysłowami, ponieważ ma postać obiektu typu Type, nie łańcucha. Obiekt klasy Type jest egzemplarzem typu Function zarejestrowanym w bibliotece Microsoft AJAX Library z wykorzystaniem metody registerClass, registerInterface lub registerEnum (tak jak nasz komponent ErrorHandler). 156 Rozdział 3. Komponenty W przedstawionym przykładzie pierwszym działaniem metody Sys.Component.create jest sprawdzenie, czy za pośrednictwem parametru type przekazano obiekt (w tym przy- padku ErrorHandler) typu Type, który dziedziczy po klasie Sys.Component. Po sprawdzeniu tego parametru metoda Sys.Component.create tworzy nowy egzemplarz typu ErrorHandler i przypisuje go pewnej zmiennej lokalnej. UWAGA Rejestrowanie obiektów implementujących interfejs IDisposable Podczas tworzenia egzemplarza komponentu, nowy obiekt jest rejestrowany w obiek- cie Sys.Application jako egzemplarz typu implementujący interfejs IDisposable. Takie rozwiązanie gwarantuje możliwość wywołania metody dispose tego egzemplarza podczas zwalniania samego obiektu Sys.Application. Omówimy to zagadnienie bardziej szczegółowo w rozdziale 4. Bezpośrednio potem następuje wywołanie metody beginUpdate już utworzonego egzem- plarza komponentu. Działanie metody beginUpdate domyślnie ogranicza się do przypisania wartości true wewnętrznej fladze aktualizacji, można jednak tę metodę przykryć w ramach implementacji nowego komponentu, aby podejmowała ewentualne dodatkowe działania. Następnie jest wywoływana metoda endUpdate nowego egzemplarza — metoda endUpdate przywraca wartość false wewnętrznej flagi _updating, po czym wykonuje przykrytą przez nas metodę initialize (która w naszej wersji dołącza do zdarzenia error obiektu window funkcję obsługującą). Po wykonaniu metody initialize następuje wy- wołanie metody updated. Jeśli nie przykryliśmy metody updated, jej oryginalna wersja nie podejmuje żadnych działań. Do zwróconego komponentu możemy uzyskiwać dostęp za pośrednictwem zmiennej, której przypisano wywołanie metody Sys.Component.create. WSKAZÓWKA Test składowej _initialized Metoda endUpdate zawiera mechanizm sprawdzający (przed wywołaniem metody initialize), czy wewnętrzna składowa _initialized reprezentuje wartość false. O ile w przypadku metody $create w chwili wywoływania endUpdate składowa _initialized zawsze ma wartość false, o tyle w razie wywołania metody endUpdate na dalszych etapach cyklu życia danego komponentu składowa _initialized będzie miała wartość true, co uniemożliwi ponowne wykonanie metody initialize. Takie rozwiązanieumożliwia nam wywoływanie metod beginUpdate i endUpdate bez ryzyka ponownej inicjalizacji naszego komponentu. Ten prosty przykład dobrze pokazuje, jak ważne są zdania niewypowiedziane. Do tej pory ani razu nie wspomniano w tym punkcie o dodawaniu naszego komponentu do zbio- ru obiektów zarządzanych przez obiekt Sys.Application (jak wiemy, za tę operację od- powiada metoda Sys.Component.create). W tym przypadku naszego komponentu nie dodano do tego zbioru, ponieważ nigdy nie ustawiono jego identyfikatora — tylko kom- Typ Sys.Component 157 ponenty z ustawionymi identyfikatorami są automatycznie dodawane do zbioru kompo- nentów zarządzanych obiektu Sys.Application. Można to zmienić, ręcznie ustawiając identyfikator komponentu i dodając go do listy komponentów zarządzanych obiektu Sys.Application (patrz listing 3.8). LISTING 3.8. Ręczne ustawienie identyfikatora komponentu i wywołanie metody addComponent var errorHandler = Sys.Component.create( ErrorHandler, null, null, null, null); errorHandler.set_id( ApplicationErrorHandler ); Sys.Application.addComponent(errorHandler); UWAGA Wywoływanie metody addComponent Gdybyśmy spróbowali ręcznie dodać dany komponent do obiektu Sys.Application bez uprzedniego ustawienia identyfikatora tego komponentu, otrzymalibyśmy błąd czasu wykonywania. Alternatywnym sposobem rozwiązania tego problemu jest początkowe ustawienie identyfikatora komponentu. Jeśli ustawimy ten identyfikator za pośrednictwem parametru properties, nasz komponent zostanie automatycznie dodany do zbioru komponentów zarządzanych przez obiekt Sys.Application bezpośrednio po przetworzeniu parametru events. Niezbędne zmiany przedstawiono na listingu 3.9. LISTING 3.9. Ustawienie identyfikatora komponentu w ramach wywołania metody Sys.Component.create var errorHandler = Sys.Component.create( ErrorHandler, {id: ApplicationErrorHandler }, null, null, null); Ponieważ określenie identyfikatora komponentu jest warunkiem jego rejestracji jako komponentu zarządzanego, niemal zawsze powinniśmy tę właściwość definiować przy okazji wywołania metody $create. Istnieją jednak specjalne przypadki, w których możemy świadomie rezygnować z ustawiania identyfikatora lub odkładać tę operację na przyszłość — zdarza się to jednak dość rzadko. 158 Rozdział 3. Komponenty Warto też pamiętać, że identyfikatory komponentów zarządzanych przez obiekt Sys.Application muszą być unikatowe. Gdybyśmy spróbowali dodać do zbioru kompo- nentów zarządzanych przez ten obiekt dwa komponenty z tym samym identyfikatorem (niezależnie od tego, czy zrobilibyśmy to za pośrednictwem wyrażenia $create, czy ręcz- nie wywołując metodę addComponent), otrzymalibyśmy błąd. UWAGA Korzystanie ze zwróconej zmiennej Metoda $create umożliwia nam dostęp do utworzonego komponentu za pośrednic- twem zwróconego przez siebie wskaźnika. Okazuje się jednak, że wspomniany znacznik jest przydatny tylko w pewnych sytuacjach. Ponieważ nasz komponent jest rejestro- wany w obiekcie Sys.Application, możemy w przyszłości uzyskać dostęp do tego komponentu albo odnajdując go w zbiorze komponentów zarządzanych przez ten obiekt, albo korzystając z jego unikatowego identyfikatora. Stosowanie parametru properties W tym przykładzie spróbujemy przekazać na wejściu metody Sys.Component.create kilka początkowych wartości właściwości. Wywołanie tej metody przedstawiono na listingu 3.10. LISTING 3.10. Przekazywanie początkowych wartości właściwości var errorHandler = Sys.Component.create( ErrorHandler, { id: ApplicationErrorHandler , disableErrorPublication: true }, null, null, null); properties Oczekiwany typ: Object Wymagany: Nie Opis: Obiekt zawierający pary klucz-wartość, gdzie klucz reprezentuje nazwę ustawianej właściwości komponentu, a wartość reprezentuje to, co tej właściwości ma zostać przypisane. W przedstawionym przykładzie początkowe kroki metody $create są takie same jak w poprzednim przykładzie. Metoda $create weryfikuje wartość parametru type, tworzy nasz komponent i wykonuje metodę beginUpdate. Typ Sys.Component 159 W kolejnym kroku metoda $create przypisuje przekazane wartości właściwościom komponentu. Właściwości i ich wartości są przekazywane na wejściu tej metody z wyko- rzystaniem składni łańcuch-obiekt (wyróżnionej pogrubieniem na listingu 3.10). Zamiast korzystać ze wspomnianej składni, można by użyć przedstawionego na listingu 3.11 kodu tworzącego obiekt, jednak właśnie składnia łańcuch-obiekt jest w tej sytuacji krótsza i bardziej czytelna. LISTING 3.11. Tworzenie obiektu właściwości z wykorzystaniem zmiennych var initialProperties = new Object; initialProperties.id = ApplicationErrorHandler ; initialProperties.disableErrorPublication = true; var errorHandler = $create( ErrorHandler, initialProperties, null, null, null); Niezależnie od stosowanej składni nasz kod wyraża dążenie do ustawienia dwóch wła- ściwości: id oraz disableErrorPublication. Aby ustawienie tych właściwości było możliwe, metoda $create deleguje sterowanie do innej metody, nazwanej Sys$Component_setProperties. Ta globalna metoda dostępna w ramach biblioteki Microsoft AJAX Library została stworzona właśnie z myślą o ustawianiu właściwości komponentów. Metoda Sys$Component_setProperties otrzymuje na wejściu dwa parametry: obiekt target i obiekt properties. W ramach tej metody każda z naszych właściwości expando dołączona do parametru properties jest odczytywana i przetwarzana według zbioru ściśle określonych reguł. Pierwsza reguła określa, czy dla danej właściwości istnieje metoda zwracająca jej war- tość. Nazwę tej metody określa się, poprzedzając nazwę samej właściwości (w tym przy- padku id) przedrostkiem get_. W naszym przykładzie metoda get_id istnieje w klasie bazowej Sys.Component, zatem warunek tej reguły jest spełniony. Po stwierdzeniu istnienia metody zwracającej metoda Sys$Component_setProperties przystępuje do poszukiwania metody ustawiającej. Nazwa tej metody powinna się składać z nazwy samej właściwości poprzedzonej przedrostkiem set_. Ponownie okazuje się, że odpowiednia metoda (w tym przypadku set_id) istnieje w klasie bazowej Sys.Component. Po stwierdzeniu istnienia metody ustawiającej metoda Sys$Component_setProperties wywołuje ją, przekazując na jej wejściu wartość bieżącej właściwości. W prezentowanym przykładzie metoda ustawiająca otrzymuje wartość ApplicationErrorHandler. Opisywany proces jest powtarzany aż do momentu skutecznego zastosowania wszyst- kich właściwości danego komponentu lub wystąpienia jakiegoś błędu (związanego na przykład z brakiem metody zwracającej, niewłaściwą liczbą parametrów obsługiwanych przez metodę ustawiającą lub wieloma innymi możliwymi problemami). W naszym przy- kładzie właściwość disableErrorPublication jest inicjalizowana z wartością true. 160 Rozdział 3. Komponenty UWAGA Iteracyjne przeszukiwanie właściwości Dostęp do właściwości expando dołączonych do parametru properties uzyskujemy, korzystając z pętli for…in. Jak wiemy z rozdziału 1., zatytułowanego „Programowanie w języku JavaScript”, pętla for…in iteracyjnie przeszukuje właściwości obiektu i umiesz- cza we wskazanej zmiennej bieżącą właściwość. Po umieszczeniu nazwy bieżącej właściwości w zmiennej, dostęp do wartości skoja- rzonej z tą nazwą można uzyskać według reguł obowiązujących w świecie tablic aso- cjacyjnych opisanych w rozdziale 1. Wywoływanie metod ustawiających Jak już wspomniano, metoda ustawiająca właściwość jest wykonywana w trakcie tworzenia obiektu komponentu. Podobnie jak w językach platformy .NET, metoda ustawiająca może zawierać dowolny kod niezbędny do usta- wienia odpowiedniej właściwości. Jeśli działanie tej metody będzie proce- sem długotrwałym, tworzenie komponentu będzie wstrzymane do czasu za- kończenia tego procesu. W tej sytuacji musimy zachować należytą ostrożność, aby metoda ustawiają- ca była możliwie efektywna i — tym samym — aby proces tworzenia kom- ponentu trwał jak najkrócej. Jeśli dysponujemy jakimś dodatkowym kodem, który jednak nie powinien być wykonywany w ramach procesu tworzenia komponentu, możemy tego wyko- nywania uniknąć, uzależniając je od wartości składowej prywatnej _updating: … set_disableErrorPublication: function(value) { if (!this.get_updating()) { this.raisePropertyChanged ( disableErrorPublication ); } this._disableErrorPublication = value; }, … W powyższym przykładzie kodu źródłowego przed wygenerowaniem zda- rzenia propertyChangedupewniamy się, że dany komponent nie jest aktuali- zowany. Sprawdzenie flagi _updating jest nieporównanie mniej kosztowne niż przechodzenie przez cały proces generowania zdarzenia. Ostrzeżenie: Przedstawiony kod ma wyłącznie charakter testowy. Przed jego użyciem powinniśmy dokładnie sprawdzić, czy w naszym przypadku zdarze- nie propertyChanged rzeczywiście nie powinno być generowane w czasie, gdy dany komponent jest aktualizowany. Typ Sys.Component 161 Ustawianie właściwości złożonych Przykłady właściwości id i disableErrorPublication naszego egzemplarza komponentu ErrorHandler ustawianych za pośrednictwem metody $create są dość proste. Okazuje się jednak, że istnieją cztery bardziej zaawansowane scenariusze ustawiania właściwości, które warto wykorzystać podczas two- rzenia złożonych komponentów w ramach pojedynczych wyrażeń: 1. Ustawianie wartości pozbawionej metody ustawiającej lub zwracającej, na przykład atrybutu elementu DOM lub właściwości dołączonej do prototypu. 2. Dopisywanie elementów do tablicy. 3. Ustawianie właściwości podkomponentu, czyli komponentu zawartego w innym komponencie. 4. Dodawanie właściwości do istniejącego obiektu. Każdy z tych scenariuszy zilustrowano w kodzie poniższego, dość mało reali- stycznego komponentu MyComplexComponent: MyComplexComponent = function() { MyComplexComponent.initializeBase(this); this.city = null; this._areaCodes = []; this._myObject = { firstName: Harry }; this.subComponent = $create(ErrorHandler, null, null, null, null); }; MyComplexComponent.prototype = { someExpandoProperty: null, get_address: function() { return this._address; }, set_address: function(value) { this._address = value; }, get_areaCodes: function() { return this._areaCodes; }, get_myObject: function() { return this._myObject; } }; MyComplexComponent.registerClass( MyComplexComponent , Sys.Component); 162 Rozdział 3. Komponenty var newComponent = $create( MyComplexComponent, { id: MyNewComplexComponent , city: Sanok , areaCodes: [619, 858, 760], someExpandoProperty: Moja wartoĂÊ wïaĂciwoĂci expando , subComponent: { id: ApplicationErrorHandler , disableErrorPublication: true }, myObject: { lastName: Nowak } }, null, null, null); 1. Ustawianie wartości, dla której nie istnieje metoda zwracająca lub ustawiająca. Ten scenariusz zilustrowano na przykładzie ustawiania właściwości city i someExpandoProperty. Tego rodzaju właściwości można ustawiać, po- nieważ stanowią istniejące pola swojego obiektu. Gdyby nie istniały, wy- rażenie setProperties by ich nie dodało. 2. Dołączanie elementów do tablicy. Drugi zaawansowany scenariusz zilustrowano na przykładzie właściwości areaCodes. W tym przypadku zdefiniowano nową tablicę złożoną z trzech elementów (619, 858 i 760) i przypisano ją właściwości areaCodes. Doda- wanie elementów do istniejącej tablicy wymaga metody zwracającej, ale nie wymaga metody ustawiającej wartość tej właściwości. W razie istnienia metody ustawiającej istnieje możliwość jej użycia zamiast kodu metody zwracającej — wówczas to kod metody ustawiającej będzie odpowiadał za dołączenie elementów do tablicy. Warto też pamiętać o konieczności uprzedniego utworzenia egzemplarza danej tablicy. Jeśli okaże się, że dana zmienna wskazuje na null lub undefined, zostanie wygenerowany błąd. 3. Ustawianie właściwości podkomponentu. Trzeci zaawansowany scenariusz przedstawiono na przykładzie właściwo- ści subComponent. W tym przypadku zdefiniowano podobiekt zawierający właściwości id oraz disableErrorPublication, które zdefiniowano wcze- śniej w ramach komponentu ErrorHandler. Kiedy metoda setProperties odkrywa te właściwości, uzyskuje dostęp do podkomponentu, po czym rekurencyjnie wywołuje metodę setProperties, stosując ten podkompo- nent w roli parametru target oraz podobiekt zawierający te właściwości w roli parametru properties. Tego rodzaju wywołania rekurencyjne mogą być wykorzystywane na dowolnej liczbie poziomów (jeśli ustawiono pa- rametr properties w odpowiedni sposób). Typ Sys.Component 163 Równie dobrze moglibyśmy tutaj zdefiniować metodę zwracającą i otrzy- mać ten sam efekt, gdybyśmy jednak dodatkowo zdefiniowali metodę ustawiającą, proces ustawiania właściwości podkomponentu nie działałby zgodnie z naszymi oczekiwaniami. Metoda setProperties wywoływana rekurencyjnie z wykorzystaniem komponentu w roli parametru target sama wywołuje metodę beginUpdate tego komponentu przed wejściem w pętlę for…in i metodę endUpdate po opuszczeniu tej pętli. Warto o tym pamiętać, jeśli w naszym kodzie korzy- stamy z metody get_updating. 4. Ustawianie właściwości zwykłego obiektu JavaScriptu. Czwarty, ostatni zaawansowany scenariusz przedstawiono na przykładzie właściwości myObject. Właściwość myObject definiuje prosty obiekt zawie- rający właściwość lastName, która reprezentuje wartość Nowak . Kiedy metoda setProperties odkrywa tę właściwość, stosuje rekurencyjne wy- wołanie metody setProperties, aby zastosować tę nową właściwość dla składowej myObject. Tym razem zamiast przekazywać komponent za po- średnictwem parametru target, przekazujemy w roli tego parametru właściwość myObject, a obiekt nowej właściwości jest przekazywany za pośrednictwem właściwości properties. Jak widać, parametr properties metody $create może z powodzeniem obsłu- giwać kilka zaawansowanych scenariuszy. Jeśli będziesz o tej możliwości pa- miętał, być może znajdziesz dla tych rozwiązań zastosowania w swoim kodzie. Stosowanie parametru events W tym przykładzie spróbujemy wykorzystać parametr events do skojarzenia metody obsługującej z dostępnym, zainicjalizowanym zdarzeniem. Możliwy sposób realizacji tego zadania pokazano na listingu 3.12. LISTING 3.12. Przekazywanie metod obsługujących na wejściu metody $create $create( ErrorHandler, { id: ApplicationErrorHandler , disableErrorPublication: true }, { unhandledErrorOccurred: function(sender, args) { alert(args._stackTrace); } }, null, null); 164 Rozdział 3. Komponenty events Oczekiwany typ: Object Wymagany: Nie Opis: Obiekt zawierający pary klucz-wartość, gdzie klucz reprezentuje nazwę zdarzenia danego komponentu, a wartość zawiera metodę obsługującą, która ma zostać skojarzona z tym zdarzeniem. W przedstawionym przykładzie początkowe kroki podejmowane przez metodę $create są takie same jak w przykładzie ilustrującym techniki stosowania parametru properties. Metoda $createweryfikuje otrzymany typ, tworzy komponent, wykonuje metodę beginUpda- te, po czym ustawia właściwości tego komponentu. Po ustawieniu właściwości następuje przetworzenie parametru events. Podobnie jak w przypadku parametru properties, parametr events jest obiektem zawierającym zbiór par klucz-wartość. Elementy obiektu events są iteracyjnie przeszukiwane, a każda odnale- ziona para klucz-wartość dodaje metodę obsługującą do odpowiedniego zdarzenia aż do wyczerpania całego zbioru lub wystąpienia jakiegoś błędu. Następnie — także podobnie jak w przypadku parametru properties — przekazane w ten sposób metody obsługujące są dodawane do zdarzeń za pomocą odpowiedniej metody. W tym przypadku klucz, czyli unhandledErrorOccurred, jest automatycznie poprzedzany przedrostkiem add_ — otrzymujemy więc metodę add_unhandledErrorOccurred. Wspomniany łańcuch jest następnie traktowany jako funkcja należąca do definicji danego komponentu. Jeśli metodę add_unhandledErrorOccurred rzeczywiście uda się odnaleźć w tym komponencie i jeśli wartość tej pary klucz-wartość jest obiektem typu Function, obiekt ten jest przekazywany na wejściu metody add_unhandledErrorOccurred jako jej parametr (w ten sposób wskazana metoda jest dodawana do zbioru metod obsługujących dane zdarzenie). W naszym przykładzie użycia wyrażenia $create zdefiniowano metodę obsługującą zdarze- nie w ramach samego wywołania. Okazuje się, że tak zdefiniowana metoda może być z powo- dzeniem skojarzona ze zdarzeniem unhandledErrorOccurred. Alternatywnym sposobem prze- kazania tej metody jest predefiniowanie funkcji obsługującej nasze zdarzenie (patrz listing 3.13). LISTING 3.13. Predefiniowanie metody obsługującej zdarzenie function unhandledErrorHandler(sender, args) { alert(args._stackTrace); } $create( MyComponent, {address: ul. Faïszywa 123 }, { unhandledErrorOccurred: unhandledErrorHandler } }, null, null); Typ Sys.Component 165 Predefiniowanie metod obsługujących zdarzenia umożliwia ich wielokrotne wykorzy- stywanie także w innych komponentach lub wywoływanie proceduralne. Co więcej, gdybyśmy chcieli obsługiwać zdarzenie za pomocą metody zawartej w naszym komponencie (zamiast — jak w kodzie z listingu 3.13 — korzystać z funkcji globalnej), powinniśmy wykonać dodatkowy krok polegający na utworzeniu delegacji obejmującej naszą metodę obsługującą, aby kontekst wskazywał na odpowiedni komponent (patrz listing 3.14). LISTING 3.14. Opakowanie metody obsługującej w ramach delegacji MyOtherComponent = function() { MyOtherComponent.initializeBase(this); this._subComponent = null; }; MyOtherComponent.prototype = { _unhandledErrorOccurred: function(sender, args) { var stackTrace = args._stackTrace; if (typeof(stackTrace) != undefined ) { alert ( ¥lad stosu tego bïÚdu: + stackTrace); } }, initialize: function() { MyOtherComponent.callBaseMethod(this, initialize ); this._errorHandler = $create( ErrorHandler, { id: ApplicationErrorHandler , disableErrorPublishing: true }, { unhandledErrorOccurred: Function.createDelegate ( this, this._unhandledErrorOccurred ) }, null, null); // powoduje wygenerowanie błędu var nullObj = null; nullObj.causeError; } }; MyOtherComponent.registerClass( MyOtherComponent , Sys.Component); 166 Rozdział 3. Komponenty We fragmencie kodu metody initialize typu MyOtherComponent wyróżnionym po- grubieniem utworzyliśmy egzemplarz komponentu ErrorHandler. Metodę obsługującą przy- pisywaną do zdarzenia unhandledErrorOccurred umieszczamy w delegacji, aby umożliwić wykonywanie metody _unhandledErrorOccurred z wykorzystaniem właściwego kontekstu. UWAGA Przedrostki funkcji Przy okazji omawiania technik ustawiania właściwości i dodawania metod obsługują- cych zdarzenia za pomocą metody $create mogliśmy obserwować sposoby poprzedza- nia właściwości przedrostkami get_ i set_ oraz metod obsługujących przedrostkiem add_. Okazuje się, że wymienione przedrostki nie tylko mają charakter estetyczny, ale także znaczenie funkcjonalne. Stosowanie parametru references Za pośrednictwem parametru references możemy przypisać jeden komponent właściwo- ści innego komponentu i — tym samym — łączyć ze sobą różne komponenty. Być może zastanawiasz się, po co mielibyśmy wykorzystywać do tego celu odrębny parametr, skoro równie dobrze można by osiągnąć ten cel za pomocą parametru properties. Dodatkowy parametr jest niezbędny, ponieważ podczas tworzenia egzemplarzy komponentów klienckich z wykorzystaniem kodu serwera nie znamy kolejności tworzenia tych komponentów. Jeśli korzystamy z odrębnego parametru, w ramach procesu inicjalizacji realizowanego przez obiekt Sys.Application referencje do komponentów są traktowane w specjalny sposób, a ich przypisywanie następuje dopiero po utworzeniu wszystkich komponentów. W ten sposób udało się wyeliminować problemy związane z próbami uzyskiwania przez kompo- nenty dostępu do innych, jeszcze nieistniejących komponentów. Aby zilustrować znaczenie parametru references, przekazujemy jeden komponent w formie referencji do innego komponentu za pośrednictwem wspomnianego parametru wyrażenia $create. W tym celu musimy najpierw utworzyć komponent, który będzie można wykorzystać w roli referencji do naszego drugiego komponentu. Na listingu 3.15 przedstawiono dwa przykładowe wyrażenia $create. Dla jasności w prezentowanym sce- nariuszu posłużyliśmy się dwoma fikcyjnymi komponentami. LISTING 3.15. Przypisywanie referencji // tworzy pierwszy komponent $create( MyComponent, { id: MyFirstComponent }, null null, null ); Typ Sys.Component 167 // tworzy drugi komponent i przypisuje go właściwości nazwanej // subComponent i należącej do pierwszego komponentu $create( MyComponent, { id: MySecondComponent }, null, { subComponent: MyFirstComponent }, null ); references Oczekiwany typ: Object Wymagany: Nie Opis: Obiekt zawierający pary klucz-wartość, gdzie klucz reprezentuje właściwość komponentu, a wartość reprezentuje komponent, który tej właściwości ma zostać przypisany. Wartość powinna zawierać identyfikator tego komponentu. Po wykonaniu kodu odpowiedzialnego za przypisywanie zdarzeń metoda $create przystępuje do przetwarzania parametru references. Podobnie jak parametry properties i events, parametr references ma postać obiektu z parami klucz-wartość, gdzie klucz reprezentuje właściwość, której chcemy przypisać komponent, a wartość reprezentuje iden- tyfikator komponentu, który ma zostać przypisany tej właściwości. Na listingu 3.15 obiekt przekazywany za pośrednictwem parametru references wy- różniono pogrubieniem. Jak widać, chcemy przypisać właściwości subComponent tworzonego komponentu komponent z identyfikatorem MyFirstComponent. Podobnie jak omówiona wcześniej metoda setProperties, metoda setReferences szuka metody ustawiającej, której nazwa odpowiada nazwie metody danej właściwości poprzedzonej przedrostkiem set_. W tym przypadku będzie to metoda nazwana set_subComponent. Po odnalezieniu tej metody wyrażenie $create szuka w zbiorze komponentów zarządzanych przez obiekt Sys.Application komponentu z identyfikatorem MyFirstComponent. Jeśli uda się odnaleźć ten komponent, zostanie wywołana metoda ustawiająca set_subComponent, której parametr będzie reprezentował znaleziony komponent. UWAGA Odnajdywanie komponentów zarządzanych Za pośrednictwem metody Sys.Application.find możemy odnajdywać zarejestrowa- ne komponenty według identyfikatorów. Przy okazji szczegółowego omawiania klasy Sys.Application (w rozdziale 4.) przyjrzymy się uważnie między innymi jej me- todzie find. 168 Rozdział 3. Komponenty WSKAZÓWKA Kolejność tworzenia komponentów Jak już wspomniano, warunkiem prawidłowego działania tego kodu jest dostęp- ność egzemplarza komponentu MyFirstComponent przed wykonaniem drugiego wy- rażenia $create. Referencje do nieistniejących komponentów mogą być stosowane tylko wte
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

ASP.NET AJAX Server Controls. Zaawansowane programowanie w nurcie .NET Framework 3.5. Microsoft .NET Development Series
Autor:
,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: