Angular, cz. III - Instytut Informatyki Teoretycznej i Stosowanej
Transkrypt
Angular, cz. III - Instytut Informatyki Teoretycznej i Stosowanej
Angular, cz. III Angular, cz. III Tworzenie serwisów Web 2.0 dr inż. Robert Perliński [email protected] Politechnika Częstochowska Instytut Informatyki Teoretycznej i Stosowanej 21 marca 2016 1/42 Plan prezentacji 1 Usługi w Angular $log $window $document $interval, $timeout 2 Własne usługi 3 Deployd Angular, cz. III 2/42 AngularJS Strona projektu: https://angularjs.org/ Dokumentacja: https://docs.angularjs.org/api Tutorial: https://docs.angularjs.org/tutorial Przewodnik dla developerów: https://docs.angularjs.org/guide Kurs na CodeSchool: http://angular.codeschool.com/ Angular, cz. III 3/42 Usługi i moduły Usługi w Angular: używane w celu hermetyzacji funkcjonalności, która ma być ponownie wykorzystana, a nie posuje do wzorca MVC, często dotyczą zagadnień przekrojowych (funkcjonalność związana z wieloma komponentami), przykłady: rejsetracja danych w dzienniku zdarzeń, zapewnienie bezpieczeństwa, obsługa sieci. Dwie role modułów: zdefiniowanie funkcjonalności aplikacji stosowanej w elementach HTML przez dyrektywę ng-app, zdefiniowanie funkcjonalności (usługa, dyrektywa, filrt) w sposób ułatwiający jego ponowne użycie w innych aplikacjach. Wczytywanie wszystkich modułów odbywa się przed sprawdzaniem zależności. Angular, cz. III 4/42 Usługi w Angular I Usługi w Angular są wykorzystywane do wykonywania najczęstszych działań. Lista większości z nich: Nazwa $anchorScroll $animate $compile $controller $document $exceptionHandler $filter $http $injector $interpolate $interval $location Angular, cz. III Opis Przewija okno przeglądarki internetowej do wskazanego punktu Animacja zmian (transformacji) zawartości Przetwarza fragment kodu HTML w celu utworzenia funkcji, którą można wykorzystać do generowania zawartości Opakowanie dla usługi $injector tworzącej egz. kontrolerów Dostarcza obiekty jqLite zawierające obiekt window.document modelu DOM Obsługuje wyjątki, które pojawiają się w aplikacji Zapewnia dostęp do filtrów Tworzy żądania AJAX i zarządza nimi Tworzy egzemplarz komponentów AngularJS Przetwarza ciąg tekstowy zawierający wyrażenie dołączania danych; pozwala na tworzenie funkcji do generowania zawartości Opakowuje funkcję window.setInterval() Zapewnia opakowanie dla obiektu location przeglądarki internetowej 5/42 Usługi w Angular II Druga częśc listy: Nazwa $log $parse $provide $q $resource $rootElement $rootScope $route $routeParams $sanitize $sce $swipe $timeout $window Angular, cz. III Opis Opakowuje globalny obiekt konsoli Przetwarza wyrażenie w celu utworzenia funkcji, którą można wykorzystać do generowania zawartości Implementuje wiele metod udostępnianych przez obiekt Module Zapewnia odroczone obiekty i obietnice Obsługa pracy z API RESTful Dostęp do głównego elementu w modelu DOM Dostęp do głównego zakresu Obsługa zmiany zawartości widoku na podstawie ścieżki adresu URL w przeglądarce internetowej Dostarcza informacje o trasach adresów URL Zastępuje niebezpieczne znaki HTML odpowiednikami bezpiecznymi do wyświetlenia Usuwa z ciągów tekstowych HTML niebezpieczne elementy i atrybuty aby stały się bezpieczne do wyświetlania Rozpoznaje gest machnięcia (dla ekranów dotykowych) Rozbudowane opakowanie dla funkcji window.setTimeout() Zapewnia odniesienie do obiektu window modelu DOM 6/42 Dostęp do globalnych obiektów API DOM Usługi związane z obiektami globalnymi: udostępniają API DOM przeglądarki internetowej spójne z Angular i jqLite, lista: $anchorScroll, $document, $interval, $location, $log, $timeout, $window, używane głównie ze względu na testowanie aplikacji, które wymaga małych fragmentów kodu bez powiązania z obiektami globalnymi (document, window). Angular, cz. III 7/42 Usługa $filter Usługa $filter - usługa pozwalająca na filtrowanie danych zwracanych użytkownikowi: $filter(name); name to nazwa funkcji filtrującej. Zawartość kontrolera: app.controller(’FilterController’, function($scope, $filter) { $scope.tekstOryginalny = ’hello’; $scope.tekstFiltrowany = $filter(’uppercase’)($scope.tekstOryginalny); }); Zawartość widoku: <div ng-controller="FilterController"> <h3>{{ tekstOryginalny }}</h3> <h3>{{ tekstFiltrowany }}</h3> </div> Angular, cz. III 8/42 Usługa $log $log - prosta usługa umożliwiająca pisanie logów (domyślnie do konsoli przeglądarki). Zawartość kontrolera: app.controller(’LogController’, [’$scope’, ’$log’, function($scope, $log) { $scope.llog = $log; $scope.message = ’Hello World!’; }]); Zawartość widoku: <div ng-controller="LogController"> <p>Przeładuj stronę z otwartą konsolą, wprowadź tekst i naciśnij przycisk...</p> <label>Wiadomość: <input type="text" ng-model="message" /></label> <button ng-click="llog.log(message)">log</button> <button ng-click="llog.warn(message)">warn</button> <button ng-click="llog.info(message)">info</button> <button ng-click="llog.error(message)">error</button> <button ng-click="llog.debug(message)">debug</button> </div> Angular, cz. III 9/42 Usługa $window $window: pozwala na dostęp do globalnego obiektu window, Angular nie dodaje dodatkowej funkcjonalności ani nic nie upsrawnia, pracujemy na niej tak samo jakby odbywało się to bezpośrednio na API DOM. Zawartość kontrolera: app.controller(’serviceCtrl’, function($scope, $window){ var okno = null; $scope.displayAlert = function(msg) { $window.alert(msg); }; $scope.openWindow = function(url) { okno = $window.open(url); }; $scope.closeWindow = function() { okno.close(); }; }); Zawartość widoku: <body ng-controller="serviceCtrl" class="well"> <div> <button ng-click="displayAlert(’Kliknięty!’)"> Wiadomość </button> <button ng-click="openWindow(’http://pcz.pl’)"> Otwórz PCz </button> <button ng-click="closeWindow()"> Zamknij </button> </div> </body> Angular, cz. III 10/42 Usługa $document $document: obiekt sqLite zawierający globalny obiekt API DOM window.document, za jej pomocą można odpytywać drzewo DOM z użyciem sqLite Zawartość kontrolera: app.controller(’serviceCtrl2’, function($scope, $window, $document){ $document.find("button").on("click", function(event) { $window.alert("Hello " + event.target.innerText); }); }); Zawartość widoku: <div ng-controller="serviceCtrl2" class="well"> <button>Przycisk</button> </div> Angular, cz. III 11/42 Usługi $interval i $timeout $interval i $timeout: zapewniają dostęp do funkcji windows.setInterval() oraz windows.setTimeout(), zawierają pewne usprawnienia ułatwiające pracę w Angular, jednorazowo (timeout) lub wielokrotnie (interval) opóźniają uruchomienie funkcji o określony czas. Argument fn delay count invokeApply Angular, cz. III Opis Funkcja, której wykonanie zostanie opóźnione Liczba milisekund, o którą nastąpi opóźnienie wykonania funkcji wskazanej w fn Liczba powtórzeń cyklu opóźnienie/wykonanie (tylko w $interval). Wartość domyślna 0 oznacza brak ograniczeń. Wartość domyślan (true) oznacza, że funkcja z argumentu fn będzie wykonywana w zakresie metody scope.$apply(). 12/42 Usługi $interval i $timeout Zawartość kontrolera: app.controller(’serviceCtrl3’, function($scope, $interval){ $interval(function() { $scope.time = new Date().toTimeString(); }, 2000, 5); }); Zawartość widoku: <div ng-controller="serviceCtrl3" class="panel panel-default"> <h4 class="panel-heading">Czas</h4> <div class="panel-body"> Czas: {{time}} </div> </div> Angular, cz. III 13/42 Przykład z rejestracja zdarzeń app.js var app = angular.module(’hello’, []); app.controller(’defaultCtrl’, function($scope){ $scope.data = { miasta: ["Poznań", "Kraków", "Warszawa"], klikniecia: 0 }; $scope.$watch(’data.klikniecia’, function(nowaLicz) { console.log("Całkowita liczba kliknięć: " + nowaLicz); }); }); app.directive("trzyPrzyciski", function() { return { scope: { counter: "=counter" }, link: function (scope, element, attrs) { element.on("click", function(event) { console.log("Kliknięcie przycisku: " + event.target.innerText); scope.$apply( function() { scope.counter++; } ); }); } } }); Angular, cz. III 14/42 Przykład z rejestracja zdarzeń index.html <body ng-controller="defaultCtrl"> <div> <div trzy-przyciski counter="data.klikniecia" source="data.miasta"> <button ng-repeat="miasto in data.miasta"> {{miasto}} </button> </div> <h5>Liczba kliknięć: {{data.klikniecia}}</h5> </div> </body> Angular, cz. III 15/42 Ręczne uaktualnianie zakresu Nie zawsze automatyczne auktualnianie zakresu przez Angular się sprawdza, np. przy integracji z innym frameworkiem JS (u nas jQuery) Są trzy metody, które pozwalają na rejestrację funkcji: odpowiadających na zmiany zachodzące w zakresie wstrzykujących w zakresie zmiany wprowadzone poza kodem Angular. Nazwa $apply(wyrazenie) $watch(wyrazenie, proc obslugi) $watchCollection(obiekt, proc obslugi) Angular, cz. III Opis Wprowadza zmianę w zakresie Rejestruje procedurę obsługi, która będzie powiadamiana, gdy zmianie ulegnie wartość we wskazanym wyrażeniu Rejestruje procedurę obsługi, która będzie powiadamiana, gdy zmianie ulegnie wartość dowolnej właściwości we wskazanym obiekcie 16/42 Integracja wychodząca - watch Metoda $watch: rejestruje funkcję obsługi wywołaną po zmianie wartości w zakresie. Można wskazać właściwość przycisku, nazwę jakiejś zmiennej,... zapewnia integrację wychodzącą, zmiana danej w zakresie może zmienić coś określonego funkcją, np. wygląd przycisku, z użyciem jQuery. $scope.$watch(’data.klikniecia’, function(nowaWar, staraWar) { console.log("Całkowita liczba kliknięć PRZED: " + staraWar); console.log("Całkowita liczba kliknięć PO: " + nowaWar); }); Angular, cz. III 17/42 Integracja przychodząca - apply Metoda $apply: oferuje integrację przychodzącą, jak zmienimy coś w innym frameworku to zmieni się też wartość zmiennej w Angular przyjmuje również funkcje. Przy własnych dyrektywach można taką funkcją uaktualnić zmienną z zakresu w odpowiedzi na działania użytkownika. app.directive("trzyPrzyciski", function() { return { scope: { counter: "=licznik" }, link: function (scope, element, attrs) { element.on("click", function(event) { console.log("Kliknięcie przycisku: " + event.target.innerText); scope.$apply(function() { scope.counter++; }); }); } } }); <div trzy-przyciski licznik="data.klikniecia" ... Angular, cz. III 18/42 Integracja przychodząca - apply 1. data.klinkiecia jest w kontrolerze 2. w dyrektywie obiekt scope wiąże nazwę (counter) pola z nazwą atrybutu z widoku (licznik) 3. w widoku jest powiązana nazwa atrubutu z nazwą rzeczywistej zmiennej 4. kliknięcie na przycisk (JQuery) powoduje zmianę wartości zmiennej data.klikniecia app.directive("trzyPrzyciski", function() { return { scope: { counter: "=licznik" }, link: function (scope, element, attrs) { element.on("click", function(event) { console.log("Kliknięcie przycisku: " + event.target.innerText); scope.$apply(function() { scope.counter++; }); }); } } }); <div trzy-przyciski licznik="data.klikniecia" ... Angular, cz. III 19/42 Metody obiektu Module Nazwa animation(nazwa, funkcja fabryki) config(wywolanie zwrotne) constant(klucz, wartosc) controller(nazwa, konstruktor) directive(nazwa, funkcja fabryki) factory(nazwa, funkcja fabryki) filter(nazwa, funkcja fabryki) provider(nazwa, typ) name run(wywolanie zwrotne) service(nazwa, konstruktor) value(nazwa, wartość) Opis Obsługuje funkcję animacji Rejestruje funkcję, która może zostać użyta w celu skonfigurowania modułu podczas jego wczytywania Definiuje usługę zwracającą stałą Tworzy kontroler Tworzy dyrektywę Tworzy usługę Tworzy filtr formatujący dane wyświetlane użytkownikowi Tworzy usługę zwraca nazwę modułu Rejestruje funkcję wywołaną po wczytniu Angular oraz skonfigurowaniu wszystkich modułów Tworzy usługę Definiuje usługę zwracającą stałą constant() i value() są usługami o bardzo prostej funkcjonalności. Angular, cz. III 20/42 Tworzenie własnych usług Trzy sposoby na tworzenie własnych usług: factory() service() provider() Powyższe metody: dają taki sam wynik - funkcjonalność gotową do użycia w Angular, mają różne sposoby towrzenia obiektu usługi i zarządzanie nim. Angular, cz. III 21/42 Metoda Module.factory() factory(nazwa, funkcja fabryki): najprostsza z dostępnych metod, przyjmuje dwa parametry: nazwa - określa jak usługa będzie się nazywać, funkcja fabryki - zwraca obiekt usługi. Uwagi: funkcja fabryki wywoływana tylko raz (singleton) - każdy odbiorca usługi korzysta z tego samego obiektu, zwrócana wartość to obiekt usługi - można wykorzystać wszędzie, gdzie chcemy, nasza własna usługa do rejestracji danych, zamiast $log nowa usługa o tej samej nazwię zastąpi istniejącą, usługi wbudowane w Angular zaczynają się od $ - uniknięcie konfliktu nazw. Angular, cz. III 22/42 Przykład metody factory() uslugi.js angular.module("mojeUslugi", []) .factory("logService", function() { var liczbaWiadomosci = 0; return { log: function (wiadomosc) { console.log("(LOG " + liczbaWiadomosci++ + ") " + wiadomosc); } } }) tworzony jest nowy moduł definiujący usługę, metoda factory() tworzy usługę logService wartość zwrotna - obiekt definiujący funkcję log(), która przyjmuje jeden parametr, komunikat zmienna liczbaWiadomosci jest wspólna dla użytkowników naszej usługi - singleton Angular, cz. III 23/42 Factory: wykorzystanie naszej usługi app.js var app = angular.module(’uslugiApp’, ["mojeDyrektywy", "mojeUslugi"]); app.controller(’defaultCtrl’, function($scope, logService){ $scope.data = { miasta: ["Poznań", "Kraków", "Warszawa"], klikniecia: 0 }; $scope.nazwa = app.name; $scope.$watch(’data.klikniecia’, function(nowaLicz) { // console.log("Całkowita liczba kliknięć: " + nowaLicz); logService.log("Całkowita liczba kliknięć: " + nowaLicz); }); }); wykorzystanie modułu mojeUslugi wykorzystanie usługi logService w kontrolerze zamiana console.log() na logService.log() analogiczne zmiany wykonujemy w naszej dyrektywie Angular, cz. III 24/42 Metoda Module.service() service(nazwa, konstruktor): funkcja zwrócona przez service jest używana jako kontruktor do utworzenia usługi, tworzenie nowego obiektu (usługi) za pomocą słowa new, funkcja konstruktora jest szablonem dla nowych obiektów. uslugi.js var uslugaBazowa = function() { this.liczbaWiadomosci = 0; this.log = function(wiadomosc) { console.log(this.typWiad + ": " + (this.liczbaWiadomosci++) + ") " + wiadomosc); } }; ... uslugaBazowa to zmienna konstruktora, zawiera zmienną liczbaWiadomosci oraz metodę log() log() używa zmiennej typWiad - niezidentyfikowanej na razie Angular, cz. III 25/42 Metoda Module.service() uslugi.js ... var uslugaError = function() { }; uslugaError.prototype = new uslugaBazowa(); uslugaError.prototype.typWiad = "Error"; angular.module("mojeUslugi", []) .service("logService", uslugaDebug) .service("errorService", uslugaError); utworzenie nowej funkcji konstruktora o nazwie uslugaError(), ustawienie prototypu konstruktora jako nowego obiektu - new uslugaBazowa(), nowy obiekt zawiera skopiowane właścowiści i funkcje z szablonu, jednokrotne definiowanie funkcjonalności i używanie jej w różnych obiektach na końcu jest rejestracja komponentów jak usług service() Angular, cz. III 26/42 Przykład metody service() - całość uslugi.js var uslugaBazowa = function() { this.liczbaWiadomosci = 0; this.log = function(wiadomosc) { console.log(this.typWiad + ": " + (this.liczbaWiadomosci++) + ") " + wiadomosc); } }; var uslugaDebug = function() { }; uslugaDebug.prototype = new uslugaBazowa(); uslugaDebug.prototype.typWiad = "Debug"; var uslugaError = function() { }; uslugaError.prototype = new uslugaBazowa(); uslugaError.prototype.typWiad = "Error"; angular.module("mojeUslugi", []) .service("logService", uslugaDebug) .service("errorService", uslugaError); Angular, cz. III 27/42 Service: wykorzystanie naszej usługi app.js var app = angular.module(’uslugiApp’, ["mojeDyrektywy", "mojeUslugi"]); app.controller(’defaultCtrl’, function($scope, logService, errorService){ $scope.data = { miasta: ["Poznań", "Kraków", "Warszawa"], klikniecia: 0 }; $scope.nazwa = app.name; $scope.$watch(’data.klikniecia’, function(nowaLicz) { logService.log("Całkowita liczba kliknięć: " + nowaLicz); errorService.log("Całkowita liczba kliknięć: " + nowaLicz*10); }); }); wykorzystanie modułu mojeUslugi wykorzystanie usługi logService oraz errorService zamiana console.log() na logService.log() i errorService.log() Angular, cz. III 28/42 Service w działaniu Angular, cz. III 29/42 Deployd - jak szybko zbudować własne API Deployd (http://deployd.com/) - proste narzędzie pozwalające na szybkie utworzenie własnego API. Angular, cz. III 30/42 Deployd - właściwości kolekcji Jest wybór spośród 5 typów danych: string, number, boolean, object, array. Można oznaczyć czy pole jest wymagane (Required). Automatycznie tworzona właściwość id. Angular, cz. III 31/42 Deployd - wprowadzanie danych Można łatwo dodać, usunąć bądź zmodyfikować dane. Angular, cz. III 32/42 Deployd - dostępne API Dla każdej składowanej kolekcji można wyświetlić listę dostępnego API Angular, cz. III 33/42 Deployd - dodawanie użytkowników Można tworzyć kolekcje użytkowników. Każdy użytkownik ma obowiązkowo login i hasło. Do kolekcji można dodawać dodatkowe właściwości. Angular, cz. III 34/42 Deployd - API użytkowników Oprócz podstawowych operacji API udostępnia: logowanie (/user/login, wymaga loginu i hasła). wylogowywanie (/user/logout). pobranie informacji o sobie (user/me). Angular, cz. III 35/42 Przykładowe API dla kolekcji produkty Zadanie Pobieranie danych Tworzenie obiektu Pobieranie obiektu Aktualizacja obiektu Usuwanie obiektu Metoda GET Ścieżka /produkty Przyjmuje nic POST /produkty GET /produkty/<id> pojedynczy obiekt nic PUT /produkty/<id> DELETE /produkty/<id> pojedynczy obiekt pojedynczy obiekt Zwraca tablica obiektów zapisany obiekt (albo błąd) pojedynczy obiekt zapisany obiekt (albo błąd) nic Inne metody protokołu HTTP do wykorzystania: HEAD, TRACE, OPTIONS, CONNECT, PATCH. Angular, cz. III 36/42 Dane dostępne prze HTTP http://localhost:5500/produkty/ [{"nazwa":"Piłka","opis":"Zatwierdzone przez FIFA rozmiar i waga.", "kategoria":"Piłka nożna","cena":34,"id":"f31ddf86acde5941"}, {"nazwa":"Stadion","opis":"Składany stadion na 35 000 osób.", "kategoria":"Piłka nożna","cena":820000,"id":"8db5dce5281b0849"}, {"nazwa":"Namiot","opis":"Bardzo fajny namiot dla dwóch osób", "kategoria":"Turystyka","cena":533,"id":"dd4c567a0aa2a807"}, {"nazwa":"Nóż","opis":"Bardzo ostry i bardzo przydatny :)", "kategoria":"Turystyka","cena":89.99,"id":"825f09c954395857"}, {"cena":932.5,"kategoria":"Sporty wodne","nazwa":"Kajak jednoosobowy", "opis":"Łódka przeznaczona dla jednej osoby.","id":"68ea03bf5d7d0936"}] http://localhost:5500/produkty/dd4c567a0aa2a807 {"nazwa":"Namiot","opis":"Bardzo fajny namiot dla dwóch osób", "kategoria":"Turystyka","cena":533,"id":"dd4c567a0aa2a807"} http://localhost:5500/robert/me [{"username":"rperlinski","id":"d9a9ed20e0dec9e0"}] Angular, cz. III 37/42 Pobieranie danych - GET Widok wyświetlający dane o produkcie: <body ng-controller="SklepController"> <h1>Produkty</h1> <div ng-repeat="prod in produkty"> <h3>{{prod.nazwa}} <em>{{prod.cena | currency}}</em></h3> <p>{{prod.id}}<b>{{prod.kategoria}}</b>-{{prod.opis}}</p> </div> ... Kod kontrolera obsługujący pobranie produktów: app.controller(’SklepController’, function($scope, $http){ $scope.produkty = {}; $http.get("http://localhost:5500/produkty") .success(function(data){ $scope.produkty = data; }) .error(function(error){ $scope.error = error; }); }); Angular, cz. III 38/42 Aktualizacja danych - PUT Formularz pobierający dane o produkcie: <div ng-repeat="prod in produkty"> <form name="RzeczFormularz" ng-controller="DeleteController" ng-submit="RzeczFormularz.$valid && zmienProdukt(prod.id, prod)" novalidate> Nazwa produktu: <input ng-model="prod.nazwa" type="text" value="{{prod.nazwa}}" required /> Opis, kategoria, cena: <input ng-model="prod.opis" type="text" value="{{prod.opis}}" required /> <input ng-model="prod.kategoria" type="text" value="{{prod.kategoria}}" required /> <input ng-model="prod.cena" type="number" value="{{prod.cena}}" min="0" required /> <input type="submit" value="Zmień produkt" /> </form> </div> Kod kontrolera obsługujący pobranie produktów: app.controller(’SklepController’, function($scope, $http){ $scope.produkty = {}; $scope.zmienProdukt = function(idProduktu,produkt) { $http.put("http://localhost:5500/produkty/"+idProduktu,produkt) .success(function(data){ console.log(data); }) .error(function(error){ $scope.error = error; }); }; }); Angular, cz. III 39/42 Dodawanie danych - POST Formularz pobierający dane o produkcie: <form name="DodajFormularz" ng-controller="PushController" ng-submit="DodajFormularz.$valid && dodajProdukt()" novalidate> <input ng-model="produkt.nazwa" type="text" required /> <textarea ng-model="produkt.opis" required></textarea> <textarea ng-model="produkt.kategoria" required></textarea> <input ng-model="produkt.cena" type="number" min="0" required /> <input type="submit" value="Dodaj produkt" /> </form> Kod kontrolera obsługujący wstawienie obiektu: app.controller(’PushController’, function($scope, $http){ $scope.produkt = {}; var idProduktu = 0; $scope.dodajProdukt = function(){ $http.post("http://localhost:5500/produkty",$scope.produkt) .success(function(data){ idProduktu = data.id; }) .error(function(error){ $scope.error = error; }); }; }); Angular, cz. III 40/42 Usuwanie danych - DELETE Formularz usuwający produkt z podanym ID: <form name="UsunFormularz" ng-controller="DeleteController" ng-submit="UsunFormularz.$valid && usunProdukt(produkt.id)" novalidate> <input ng-model="produkt.id" type="text" required /> <input type="submit" value="Usuń produkt" /> </form> Kod kontrolera obsługujący usunięcie obiektu: app.controller(’DeleteController’, function($scope, $http){ $scope.produkt = {}; $scope.usunProdukt = function(idProduktu){ $http.delete("http://localhost:5500/produkty/"+idProduktu) .success(function(data){ console.log(data); }) .error(function(error){ $scope.error = error; }); }; }); Angular, cz. III 41/42 Formularze Angular, cz. III 42/42