Jak stworzyć i wdrożyć aplikację kątową?
Opublikowany: 2022-03-10Angular to jeden z popularnych wyborów podczas tworzenia nowych aplikacji internetowych. Co więcej, specyfikacje „Material Design” stały się dzisiaj wyborem do tworzenia minimalnych i wciągających wrażeń. Dlatego każdy nowy projekt „Kątowy” w większości korzysta z „Biblioteki projektowania materiałów kątowych”, aby używać komponentów zgodnych ze specyfikacjami projektowania materiałów. Od płynnych animacji po prawidłowe informacje zwrotne o interakcji, wszystko to jest już dostępne jako część oficjalnej biblioteki projektowania materiałów dla kąta.
Po opracowaniu aplikacji internetowej kolejnym krokiem jest jej wdrożenie. W tym momencie pojawia się „Netlify”. Dzięki bardzo łatwemu w użyciu interfejsowi, automatycznemu wdrażaniu, podziałowi ruchu na potrzeby testów A/B i różnym innym funkcjom, Netlify jest z pewnością świetnym narzędziem.
Artykuł będzie przewodnikiem po tworzeniu aplikacji internetowej Angular 8 przy użyciu oficjalnej biblioteki Angular Material Design. Będziemy tworzyć aplikację internetową do generowania kodów QR, całkowicie opartą na Angularze, hostowaną na Netlify.
Pliki do tego samouczka można znaleźć w serwisie GitHub, a wersja demonstracyjna jest tutaj wdrożona.
Pierwsze kroki
- Zainstaluj kątowy 8,
- Utwórz konto GitHub,
- Zainstaluj Git na swoim komputerze,
- Utwórz konto Netlify.
Uwaga : Będę używał VSCode i Microsoft Windows jako preferowanego IDE i systemu operacyjnego, chociaż kroki byłyby podobne dla każdego innego IDE w dowolnym innym systemie operacyjnym.
Po spełnieniu powyższych warunków wstępnych zacznijmy!
Makiety i planowanie
Zanim zaczniemy tworzyć projekt, warto zaplanować z wyprzedzeniem: Jaki rodzaj interfejsu użytkownika chcielibyśmy mieć w naszej aplikacji? Czy będą jakieś elementy wielokrotnego użytku? Jak aplikacja będzie współdziałać z usługami zewnętrznymi?
Najpierw sprawdź makiety interfejsu użytkownika.
Są to trzy różne strony, które będą zawarte we wniosku. Strona główna będzie punktem wyjścia naszej aplikacji. Tworzenie strony QR powinno zajmować się tworzeniem nowego kodu QR. Strona Historia pokaże wszystkie zapisane kody QR.
Makiety nie tylko dają wyobrażenie o wyglądzie aplikacji, ale także rozdzielają odpowiedzialność za każdą stronę.
Jedna obserwacja (z prób) jest taka, że wydaje się, że górny pasek nawigacyjny jest wspólny dla wszystkich stron. W ten sposób pasek nawigacyjny można utworzyć jako element wielokrotnego użytku i ponownie wykorzystać.
Teraz, gdy mamy już trochę pomysłu na to, jak aplikacja będzie wyglądać i co można ponownie wykorzystać, zacznijmy.
Tworzenie nowego projektu Angular
Uruchom VSCode, a następnie otwórz okno terminala w VSCode, aby wygenerować nowy projekt Angular.
Terminal otworzy się z domyślną ścieżką, jak pokazano w monicie. Możesz przejść do preferowanego katalogu przed kontynuowaniem; w przypadku Windowsa skorzystam z polecenia cd
.
Idąc dalej, angular-cli ma polecenie do generowania nowych projektów ng new <project-name>
. Po prostu użyj dowolnej nazwy projektu, którą lubisz i naciśnij enter, np. ng new qr
.
To uruchomi magię kątowego-cli; zapewni kilka opcji konfiguracji niektórych aspektów projektu, na przykład dodanie routingu kątowego. Następnie na podstawie wybranych opcji wygeneruje cały szkielet projektu, który można uruchomić bez żadnych modyfikacji.
W tym samouczku wpisz Yes dla routingu i wybierz CSS dla stylizacji. Spowoduje to wygenerowanie nowego projektu Angular:
Mamy teraz w pełni działający projekt Angulara. Aby upewnić się, że wszystko działa poprawnie, możemy uruchomić projekt, wpisując w terminalu polecenie: ng serve
. Uh oh, ale czekaj, powoduje to błąd. Co mogłoby się stać?
Nie martw się. Za każdym razem, gdy tworzysz nowy projekt za pomocą angular-cli, generuje on cały szkielet w folderze o nazwie zgodnej z nazwą projektu określoną w poleceniu ng new qr
. Tutaj będziemy musieli zmienić bieżący katalog roboczy na ten właśnie utworzony. W systemie Windows użyj polecenia cd qr
, aby zmienić katalog.
Teraz spróbuj ponownie uruchomić projekt za pomocą ng serve
:
Otwórz przeglądarkę internetową, przejdź do adresu URL https://localhost:4200, aby zobaczyć uruchomiony projekt. Polecenie ng serve
domyślnie uruchamia aplikację na porcie 4200.
WSKAZÓWKA : Aby uruchomić go na innym porcie, używamy polecenia ng serve --port <any-port>
na przykład ng serve --port 3000
.
Gwarantuje to, że nasz podstawowy projekt Angular jest gotowy i działa. Przejdźmy dalej.
Musimy dodać folder projektu do VSCode. Przejdź do menu „Plik” i wybierz „Otwórz folder” i wybierz folder projektu. Folder projektu będzie teraz wyświetlany w widoku Eksploratora po lewej stronie.
Dodawanie biblioteki materiałów kątowych
Aby zainstalować bibliotekę materiałów Angular, użyj następującego polecenia w oknie terminala: ng add @angular/material
. Spowoduje to (znowu) zadawanie pytań, takich jak między innymi, jaki motyw chcesz, czy chcesz domyślne animacje, czy wymagana jest obsługa dotykowa. Po prostu wybierzemy domyślny motyw Indigo/Pink
, Yes
do dodania biblioteki HammerJS
i animacji przeglądarki.
Powyższe polecenie konfiguruje również cały projekt, aby umożliwić obsługę komponentów materiału.
- Dodaje zależności projektu do package.json ,
- Dodaje czcionkę Roboto do pliku index.html ,
- Dodaje czcionkę ikony Material Design do twojego index.html ,
- Dodaje również kilka globalnych stylów CSS do:
- Usuń marginesy z ciała,
- Ustaw
height: 100%
w kodzie HTML i body, - Ustaw Roboto jako domyślną czcionkę aplikacji.
Aby mieć pewność, że wszystko jest w porządku, możesz uruchomić projekt ponownie w tym momencie, chociaż nie zauważysz niczego nowego.
Dodawanie strony głównej
Nasz szkielet projektu jest już gotowy. Zacznijmy od dodania strony głównej.
Chcemy, aby nasza strona główna była prosta, tak jak na powyższym obrazku. Ta strona główna wykorzystuje kilka kątowych komponentów materiału. Przeanalizujmy.
- Górny pasek to prosty element HTML
nav
, który zawiera material button style,mat-button
, z obrazem i tekstem jako jego dzieckiem. Kolor paska jest taki sam jak kolor podstawowy, który został wybrany podczas dodawania biblioteki materiałów Angular; - Wyśrodkowany obraz;
- Kolejny,
mat-button
, z tekstem jako jego dzieckiem. Ten przycisk pozwoli użytkownikom przejść do strony historii; - Do powyższego przycisku dołączony jest licznik,
matBadge
, pokazujący liczbę zapisanych przez użytkownika kodów QR; - Pływający przycisk akcji,
mat-fab
, w prawym dolnym rogu z akcentem kolorystycznym z wybranego motywu.
Odchodząc trochę, dodajmy najpierw inne wymagane komponenty i usługi.
Dodawanie nagłówka
Zgodnie z wcześniejszymi planami pasek nawigacyjny powinien zostać ponownie wykorzystany, stwórzmy go jako osobny komponent kątowy. Otwórz terminal w VSCode i wpisz ng gc header
(skrót od ng generate component header) i naciśnij Enter. Spowoduje to utworzenie nowego folderu o nazwie „header”, który będzie zawierał cztery pliki:
- header.component.css : używany do zapewnienia stylizacji dla tego komponentu;
- header.component.html : do dodawania elementów HTML;
- header.component.spec.ts : do pisania przypadków testowych;
- header.component.ts : aby dodać logikę opartą na maszynie Typescript.
Aby nagłówek wyglądał tak, jak w mockach, dodaj poniższy kod HTML w header.component.html :
<nav class="navbar" [class.mat-elevation-z8]=true> <div> <button *ngIf="showBackButton" aria-hidden=false mat-icon-button routerLink="/"> <mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon> </button> <span>{{currentTitle}}</span> </div> <button *ngIf="!showBackButton" aria-hidden=false mat-button class="button"> <img src="../../assets/qr-icon-white.png"> <span>QR Generator</span> </button> <button *ngIf="showHistoryNav" aria-hidden=false mat-button class="button" routerLink="/history"> <span>History</span> </button> </nav>
WSKAZÓWKA : Aby dodać rzędną dla dowolnego komponentu materiału, użyj [class.mat-elevation-z8]=true
, wartość rzędnej można zmienić, zmieniając wartość z , w tym przypadku jest to z8
. Na przykład, aby zmienić wysokość na 16, użyj [class.mat-elevation-z16]=true
.
W powyższym fragmencie kodu HTML używane są dwa elementy materiału Angular: mat-icon
i mat-button/mat-icon-button
. Ich użycie jest bardzo proste; najpierw musimy dodać te dwa jako moduły w naszym app.module.ts , jak pokazano poniżej:
Umożliwi nam to wykorzystanie tych dwóch elementów materiału Angular w dowolnym miejscu w dowolnym komponencie.
Do dodawania przycisków materiałów używany jest następujący fragment kodu HTML:
<button mat-button> Material Button </button>
W bibliotece materiałów Angular dostępne są różne typy materiałów przycisków, takie jak mat-raised-button
, mat-flat-button
, mat-fab
i inne; po prostu zamień przycisk mat-button
w powyższym fragmencie kodu na dowolny inny typ.
Drugim elementem jest mat-icon
który służy do wyświetlania ikon dostępnych w bibliotece ikon materiałów. Kiedy biblioteka materiałów Angular została dodana na początku, dodano również odniesienie do biblioteki ikon materiałów, co umożliwiło nam korzystanie z ikon z szerokiej gamy ikon.
Użycie jest tak proste, jak:
<mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon>
Zagnieżdżony tag <i>
może być użyty do zmiany rozmiaru ikony (tutaj jest to md-32
), co spowoduje, że ikona będzie miała 32 piksele wysokości i szerokości. Ta wartość może być md-24
, md-48
i tak dalej. Wartość zagnieżdżonego <i>
to nazwa ikony. (Nazwę można znaleźć tutaj dla dowolnej innej ikony.)
Dostępność
Ilekroć używane są ikony lub obrazy, konieczne jest, aby dostarczały one wystarczających informacji dla celów ułatwień dostępu lub dla użytkownika czytnika ekranu. ARIA (Accessible Rich Internet Applications) definiuje sposób na zwiększenie dostępności treści i aplikacji internetowych dla osób niepełnosprawnych.
Należy zauważyć, że elementy HTML, które mają swoją natywną semantykę (np. nav
), nie potrzebują atrybutów ARIA; czytnik ekranu już wiedziałby, że nav
jest elementem nawigacyjnym i odczytałby go jako taki.
Specyfikacje ARIA są podzielone na trzy kategorie: role, stany i właściwości. Załóżmy, że div
służy do tworzenia paska postępu w kodzie HTML. Nie ma żadnej rodzimej semantyki; Rola ARIA może opisywać ten widżet jako pasek postępu, właściwość ARIA może oznaczać jego charakterystykę, np. można ją przeciągać. Stan ARIA będzie opisywał jego aktualny stan, taki jak aktualna wartość paska postępu. Zobacz fragment poniżej:
<div role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"> </div>
Podobnie bardzo często używany atrybut aria: aria-hidden=true/false
jest używany. Wartość true sprawia, że ten element jest niewidoczny dla czytników ekranu.
Ponieważ większość elementów interfejsu użytkownika używanych w tej aplikacji ma natywne znaczenie semantyczne, jedynymi używanymi atrybutami ARIA są określanie stanów widoczności ARIA. Aby uzyskać szczegółowe informacje, zapoznaj się z tym.
Plik header.component.html zawiera pewną logikę do ukrywania i pokazywania przycisku Wstecz w zależności od bieżącej strony. Ponadto przycisk Home zawiera również obrazek/logo, które należy dodać do folderu /assets
. Pobierz obraz stąd i zapisz go w folderze /assets
.
Aby stylizować pasek nawigacji, dodaj poniższy css w header.component.css :
.navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 2; background: #3f51b5; display: flex; flex-wrap: wrap; align-items: center; padding: 12px 16px; } .button { color: white; margin: 0px 10px; }
Ponieważ chcemy, aby komponent nagłówka mógł być ponownie używany w innych komponentach, aby zdecydować, co powinno być wyświetlane, będziemy wymagać ich jako parametrów z innych komponentów. Wymaga to użycia dekoratora @Input()
, który połączy się ze zmiennymi, których użyliśmy w header.component.html .
Dodaj te wiersze w pliku header.component.ts :
// Add these three lines above the constructor entry. @Input() showBackButton: boolean; @Input() currentTitle: string; @Input() showHistoryNav: boolean; constructor() { }
Powyższe trzy wiązania zostaną przekazane jako parametr z innych komponentów, których będzie używał komponent nagłówka. Jego użycie stanie się bardziej jasne, gdy pójdziemy dalej.
Idąc dalej, musimy stworzyć stronę główną, która może być reprezentowana przez komponent Angular. Zacznijmy więc od stworzenia kolejnego komponentu; wpisz ng gc home
w terminalu, aby automatycznie wygenerować komponent home. Tak jak poprzednio, zostanie utworzony nowy folder o nazwie „home” zawierający cztery różne pliki. Zanim przejdziemy do modyfikacji tych plików, dodajmy trochę informacji o routingu do modułu routingu kątowego.
Dodawanie routingu
Angular umożliwia mapowanie adresu URL do określonego komponentu. Za każdym razem, gdy nastąpi nawigacja, struktura Angular monitoruje adres URL i na podstawie informacji zawartych w pliku app-routing.module.ts ; inicjuje mapowany składnik. W ten sposób różne komponenty nie muszą ponosić odpowiedzialności za inicjowanie innych komponentów. W naszym przypadku aplikacja ma trzy strony, którymi można się poruszać, klikając różne przyciski. Osiągamy to, wykorzystując obsługę routingu zapewnianą przez platformę Angular.
Komponent home powinien być punktem początkowym aplikacji. Dodajmy te informacje do pliku app-routing.module.ts .
Właściwość path
jest ustawiona jako pusty ciąg; pozwala nam to zmapować adres URL aplikacji do składnika strony głównej, coś takiego jak google.com
, który pokazuje stronę główną Google.
WSKAZÓWKA : Wartość ścieżki nigdy nie zaczyna się od „ /
”, ale zamiast tego używa pustego ciągu, mimo że ścieżka może przypominać search/coffee
.
Wracając do komponentu strony głównej, zastąp zawartość home.component.html następującym:
<app-header [showBackButton]="false" [currentTitle]=""></app-header> <app-profile></app-profile> <!-- FAB Fixed --> <button mat-fab class="fab-bottom-right" routerLink="/create"> <mat-icon> <i class="material-icons md-48">add</i> </mat-icon> </button>
Komponent domowy składa się z trzech części:
- Komponent nagłówka wielokrotnego użytku
<app-header>
, - Komponent profilu
<app-profile>
, - Pływający przycisk akcji w prawym dolnym rogu.
Powyższy fragment kodu HTML pokazuje, w jaki sposób składnik nagłówka wielokrotnego użytku jest używany w innych składnikach; po prostu używamy selektora komponentów i przekazujemy wymagane parametry.
Komponent profilu został stworzony do wykorzystania jako treść strony głównej — wkrótce go stworzymy.
Pływający przycisk akcji z ikoną +
jest rodzajem przycisku materiału kątowego typu mat-fab
w prawym dolnym rogu ekranu. Zawiera dyrektywę atrybutu routerLink
, która wykorzystuje informacje o trasie podane w app-routing.module.ts
do nawigacji. W tym przypadku przycisk ma wartość trasy jako /create , która zostanie zmapowana w celu utworzenia komponentu.
Aby przycisk tworzenia unosił się w prawym dolnym rogu, dodaj poniższy kod CSS w home.component.css :
.fab-bottom-right { position: fixed; left: auto; bottom: 5%; right: 10%; }
Ponieważ komponent profilu ma zarządzać treścią strony głównej, pozostawimy home.component.ts
nienaruszony.
Dodawanie komponentu profilu
Otwórz terminal, wpisz ng gc profile
i naciśnij enter, aby wygenerować komponent profilu. Zgodnie z wcześniejszymi planami, ten składnik obsłuży główną treść strony głównej. Otwórz profile.component.html
i zastąp jego zawartość następującym:
<div class="center profile-child"> <img class="avatar" src="../../assets/avatar.png"> <div class="profile-actions"> <button mat-raised-button matBadge="{{historyCount}}" matBadgeOverlap="true" matBadgeSize="medium" matBadgeColor="accent" color="primary" routerLink="/history"> <span>History</span> </button> </div> </div>
Powyższy fragment kodu HTML pokazuje, jak używać elementu matBadge
z biblioteki materiałów. Aby móc z niego korzystać, musimy wykonać zwykłe ćwiczenia polegające na dodaniu MatBadgeModule
do pliku app.module.ts
. Plakietki to małe obrazkowe deskryptory stanu dla elementów interfejsu użytkownika, takich jak przyciski, ikony lub teksty. W tym przypadku jest używany z przyciskiem pokazującym liczbę zapisanych przez użytkownika QR. Odznaka biblioteki materiałów Angular ma różne inne właściwości, takie jak ustawienie pozycji odznaki za pomocą matBadgePosition
, matBadgeSize
do określenia rozmiaru i matBadgeColor
do ustawienia koloru odznaki.
Do folderu zasobów należy dodać jeszcze jeden zasób graficzny: Pobierz. Zapisz to samo w folderze /assets
projektu.
Otwórz profile.component.css i dodaj to:
.center { top: 50%; left: 50%; position: absolute; transform: translate(-50%, -50%); } .profile-child { display: flex; flex-direction: column; align-items: center; } .profile-actions { padding-top: 20px; } .avatar { border-radius: 50%; width: 180px; height: 180px; }
Powyższy CSS osiągnie UI zgodnie z planem.
Idąc dalej, potrzebujemy jakiejś logiki, aby zaktualizować wartość licznika historii, ponieważ będzie ona odzwierciedlać używaną wcześniej matBadge
. Otwórz profile.component.ts i dodaj odpowiednio następujący fragment:
export class ProfileComponent implements OnInit { historyCount = 0; constructor(private storageUtilService: StorageutilService) { } ngOnInit() { this.updateHistoryCount(); } updateHistoryCount() { this.historyCount = this.storageUtilService.getHistoryCount(); } }
Dodaliśmy StorageutilService , ale do tej pory nie stworzyliśmy takiej usługi. Ignorując błąd, ukończyliśmy nasz komponent profilu, który kończy również nasz komponent strony głównej. Przyjrzymy się ponownie temu komponentowi profilu po utworzeniu naszej usługi pamięci masowej. Dobra, zróbmy to.
Lokalny magazyn
HTML5 zapewnia funkcję przechowywania danych w Internecie, która może być używana do lokalnego przechowywania danych. Zapewnia to znacznie więcej miejsca w porównaniu z plikami cookie — co najmniej 5 MB w porównaniu z 4 KB. Istnieją dwa typy przechowywania danych w sieci Web o różnym zasięgu i okresie istnienia: Lokalne i Sesyjne . Ten pierwszy może przechowywać dane na stałe, podczas gdy drugi jest tymczasowy i na jedną sesję. Decyzja o wyborze typu może być oparta na przypadku użycia, w naszym scenariuszu chcemy zapisywać między sesjami, więc wybierzemy pamięć lokalną .
Każda część danych jest przechowywana w parze klucz/wartość. Jako klucz użyjemy tekstu, dla którego generowany jest kod QR, a jako wartości obrazu QR zakodowanego jako ciąg base64. Utwórz folder encji, wewnątrz folderu utwórz nowy plik qr-object.ts i dodaj fragment kodu, jak pokazano:
Treść zajęć:
export class QR { text: string; imageBase64: string; constructor(text: string, imageBase64: string) { this.imageBase64 = imageBase64; this.text = text; } }
Za każdym razem, gdy użytkownik zapisze wygenerowany QR, utworzymy obiekt powyższej klasy i zapiszemy ten obiekt za pomocą usługi narzędzia pamięci masowej.
Utwórz nowy folder usług, będziemy tworzyć wiele usług, lepiej je pogrupować.
Zmień bieżący katalog roboczy na services, cd services
, aby utworzyć nową usługę, używając ng gs <any name>
. To jest skrót do ng generate service <any name>
, wpisz ng gs storageutil
i naciśnij enter
Spowoduje to utworzenie dwóch plików:
- storageutil.service.ts
- storageutil.service.spec.ts
Ten ostatni służy do pisania testów jednostkowych. Otwórz storageutil.service.ts i dodaj to:
private historyCount: number; constructor() { } saveHistory(key : string, item :string) { localStorage.setItem(key, item) this.historyCount = this.historyCount + 1; } readHistory(key : string) : string { return localStorage.getItem(key) } readAllHistory() : Array<QR> { const qrList = new Array<QR>(); for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); if (key && value) { const qr = new QR(key, value); qrList.push(qr); } } this.historyCount = qrList.length; return qrList; } getHistoryCount(): number { if (this.historyCount) { return this.historyCount; } this.readAllHistory(); return this.historyCount; } deleteHistory(key : string) { localStorage.removeItem(key) this.historyCount = this.historyCount - 1; }
Zaimportuj klasę qr-object, aby poprawić wszelkie błędy. Aby skorzystać z funkcji przechowywania lokalnego, nie ma potrzeby importowania niczego nowego, wystarczy użyć słowa kluczowego localStorage
, aby zapisać lub uzyskać wartość na podstawie klucza.
Teraz ponownie otwórz plik StorageutilService
, aby poprawnie zakończyć składnik profilu.
Uruchamiając projekt, widzimy, że strona główna działa zgodnie z planem.
Dodawanie Utwórz stronę QR
Mamy gotową stronę główną, choć przycisk tworzenia/dodawania nic nie robi. Nie martw się, właściwa logika została już napisana. Użyliśmy dyrektywy routerLink
, aby zmienić podstawową ścieżkę adresu URL na /create
, ale nie dodano mapowania do pliku app-routing.module.ts .
Stwórzmy komponent, który zajmie się tworzeniem nowych kodów QR, ng gc create-qr
i naciśnijmy enter, aby wygenerować nowy komponent.
Otwórz plik app-routing.module.ts i dodaj poniższy wpis do tablicy routes
:
{ path: 'create', component: CreateQrComponent },
Spowoduje to CreateQRComponent
z adresem URL /create
.
Otwórz create-qr.components.html i zastąp zawartość następującym:
<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <mat-card class="qrCard" [class.mat-elevation-z12]=true> <div class="qrContent"> <!--Close button section--> <div class="closeBtn"> <button mat-icon-button color="accent" routerLink="/" matTooltip="Close"> <mat-icon> <i class="material-icons md-48">close</i> </mat-icon> </button> </div> <!--QR code image section--> <div class="qrImgDiv"> <img *ngIf="!showProgressSpinner" src={{qrCodeImage}} width="200px" height="200px"> <mat-spinner *ngIf="showProgressSpinner"></mat-spinner> <div class="actionButtons" *ngIf="!showProgressSpinner"> <button mat-icon-button color="accent" matTooltip="Share this QR"> <mat-icon> <i class="material-icons md-48">share</i> </mat-icon> </button> <button mat-icon-button color="accent" (click)="saveQR()" matTooltip="Save this QR"> <mat-icon> <i class="material-icons md-48">save</i> </mat-icon> </button> </div> </div> <!--Textarea to write any text or link--> <div class="qrTextAreaDiv"> <mat-form-field> <textarea matInput [(ngModel)]="qrText" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="4" placeholder="Enter a website link or any text..."></textarea> </mat-form-field> </div> <!--Create Button--> <div class="createBtnDiv"> <button class="createBtn" mat-raised-button color="accent" matTooltip="Create new QR code" matTooltipPosition="above" (click)="createQrCode()">Create</button> </div> </div> </mat-card>
Powyższy fragment kodu używa wielu elementów biblioteki materiałów Angular. Zgodnie z planem ma jedno odwołanie do komponentu nagłówka, w którym przekazywane są wymagane parametry. Dalej znajduje się główna treść strony tworzenia; składa się z jednej karty materiału Angular lub karty mat-card
wyśrodkowanej i podniesionej do 12px, ponieważ [class.mat-elevation-z12]=true
.
Karta materiałowa to po prostu inny rodzaj pojemnika, którego można używać jako dowolnego innego znacznika div
. Chociaż biblioteka materiałów zapewnia pewne właściwości do rozmieszczania dobrze zdefiniowanych informacji na mat-card
, takie jak położenie obrazu, tytuł, podtytuł, opis i działanie, jak widać poniżej.
W powyższym fragmencie kodu HTML użyliśmy mat-card
tak jak każdego innego kontenera. Innym używanym elementem biblioteki materiałów jest matTooltip
; jest to po prostu kolejna łatwa w użyciu podpowiedź, wyświetlana, gdy użytkownik najedzie kursorem na element lub długo go naciśnie. Po prostu użyj poniższego fragmentu, aby wyświetlić podpowiedź:
matTooltip="Any text you want to show"
Może być używany z przyciskami ikon lub dowolnym innym elementem interfejsu użytkownika, aby przekazać dodatkowe informacje. W kontekście aplikacji wyświetla informację o przycisku zamykania ikony. Aby zmienić położenie podpowiedzi, używa się matTooltipPosition
:
matTooltip="Any text you want to show" matTooltipPosition="above"
Oprócz matTooltip
, mat-spinner
służy do pokazywania postępu ładowania. Gdy użytkownik kliknie przycisk „Utwórz”, nawiązywane jest połączenie sieciowe. Wtedy pojawia się pokrętło postępu. Gdy połączenie sieciowe wróci z wynikiem, po prostu chowamy spinner. Można go używać po prostu tak:
<mat-spinner *ngIf="showProgressSpinner"></mat-spinner>
showProgressSpinner
to zmienna logiczna, która służy do pokazywania/ukrywania pokrętła postępu. Biblioteka udostępnia również inne parametry, takie jak [color]='accent'
do zmiany koloru, [mode]='indeterminate'
do zmiany typu pokrętła postępu. Spinner nieokreślonego postępu nie pokaże postępu zadania, podczas gdy określony może mieć różne wartości, aby odzwierciedlić postęp zadania. Tutaj używany jest nieokreślony spinner, ponieważ nie wiemy, jak długo potrwa połączenie sieciowe.
Biblioteka materiałów zapewnia wariant obszaru tekstowego zgodny z wytycznymi dotyczącymi materiałów, ale można go używać tylko jako elementu potomnego mat-form-field
. Użycie obszaru tekstowego materiału jest tak samo proste, jak domyślnego HTML, jak poniżej:
<mat-form-field> <textarea matInput placeholder="Hint text"></textarea> </mat-form-field>
matInput
to dyrektywa, która pozwala natywnym tagom input
współpracować z mat-form-field
. Właściwość placeholder
umożliwia dodanie dowolnego tekstu podpowiedzi dla użytkownika.
WSKAZÓWKA : Użyj właściwości cdkTextareaAutosize
textarea, aby automatycznie zmieniać rozmiar. Użyj cdkAutosizeMinRows
i cdkAutosizeMaxRows
, aby ustawić wiersze i kolumny oraz wszystkie trzy razem, aby automatycznie zmieniać rozmiar obszaru tekstowego, aż osiągnie ustawiony limit maksymalnej liczby wierszy i kolumn.
Aby użyć wszystkich tych elementów biblioteki materiałów, musimy dodać je w pliku app.module.ts .
W kodzie HTML jest używany obraz zastępczy. Pobierz i zapisz go w folderze /assets
.
Powyższy kod HTML wymaga również stylizacji CSS, więc otwórz plik create-qr.component.ts i dodaj następujące elementy:
.qrCard { display: flex; flex-direction: column; align-items: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 20%; height: 65%; padding: 50px 20px; } .qrContent { display: flex; flex-direction: column; align-items: center; width: 100%; } .qrTextAreaDiv { width: 100%; display: flex; flex-direction: row; justify-content: center; padding: 0px 0px; position: absolute; bottom: 10%; } .createBtn { left: 50%; transform: translate(-50%, 0px); width: 80%; } .createBtnDiv { position: absolute; bottom: 5%; width: 100%; } .closeBtn { display: flex; flex-direction: row-reverse; align-items: flex-end; width: 100%; margin-bottom: 20px; } .closeBtnFont { font-size: 32px; color: rgba(0,0,0,0.75); } .qrImgDiv { top: 20%; position: absolute; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; } .actionButtons { display: flex; flex-direction: row; padding-top: 20px; }
Połączmy interfejs użytkownika z logiką. Otwórz plik create-qr.component.ts i dodaj poniższy kod, pozostawiając te linie, które już są obecne:
export class CreateQrComponent implements OnInit { qrCodeImage = '../../../assets/download.png'; showProgressSpinner = false; qrText: string; currentQR; showBackButton = true; title = 'Generate New QR Code'; showHistoryNav = true; constructor(private snackBar: MatSnackBar, private restutil: RestutilService, private storageService: StorageutilService) { } ngOnInit() { } createQrCode() { //Check if any value is given for the qr code text if (!!this.qrText) { //Make the http call to load qr code this.loadQRCodeImage(this.qrText); } else { //Show snackbar this.showSnackbar('Enter some text first') } } public loadQRCodeImage(text: string) { // Show progress spinner as the request is being made this.showProgressSpinner = true; // Trigger the API call this.restutil.getQRCode(text).subscribe(image =>{ // Received the result - as an image blob - require parsing this.createImageBlob(image); }, error => { console.log('Cannot fetch QR code from the url', error) // Hide the spinner - show a proper error message this.showProgressSpinner = false; }); } private createImageBlob(image: Blob) { // Create a file reader to read the image blob const reader = new FileReader(); // Add event listener for "load" - invoked once the blob reading is complete reader.addEventListener('load', () => { this.qrCodeImage = reader.result.toString(); //Hide the progress spinner this.showProgressSpinner = false; this.currentQR = reader.result.toString(); }, false); // Read image blob if it is not null or undefined if (image) { reader.readAsDataURL(image); } } saveQR() { if (!!this.qrText) { this.storageService.saveHistory(this.qrText, this.currentQR); this.showSnackbar('QR saved') } else { //Show snackbar this.showSnackbar('Enter some text first') } } showSnackbar(msg: string) { //Show snackbar this.snackBar.open(msg, '', { duration: 2000, }); } }
Aby zapewnić użytkownikom informacje kontekstowe, używamy również MatSnackBar
z biblioteki projektowania materiałów. Pojawia się jako wyskakujące okienko spod ekranu i pozostaje przez kilka sekund, zanim zniknie. Nie jest to element, ale raczej usługa, którą można wywołać z kodu Typescript.
Powyższy fragment z nazwą metody showSnackbar
pokazuje, jak otworzyć snackbar, ale zanim będzie można go użyć, musimy dodać wpis MatSnackBar
w pliku app.module.ts , tak jak zrobiliśmy to w przypadku innych elementów biblioteki materiałów.
WSKAZÓWKA : W najnowszych wersjach biblioteki materiałów Angular nie ma prostego sposobu na zmianę stylu paska przekąskowego. Zamiast tego należy wprowadzić dwa dodatki do kodu.
Najpierw użyj poniższego kodu CSS, aby zmienić kolory tła i pierwszego planu:
::ng-deep snack-bar-container.snackbarColor { background-color: rgba(63, 81, 181, 1); } ::ng-deep .snackbarColor .mat-simple-snackbar { color: white; }
Po drugie, użyj właściwości o nazwie panelClass
, aby ustawić styl na powyższą klasę CSS:
this.snackBar.open(msg, '', { duration: 2000, panelClass: ['snackbarColor'] });
Powyższe dwie kombinacje umożliwią niestandardowe stylizowanie komponentu paska przekąskowego biblioteki projektowania materiałów.
To kończy procedurę tworzenia strony QR, ale wciąż brakuje jednego elementu. Sprawdzenie pliku create-qr.component.ts spowoduje wyświetlenie błędu dotyczącego brakującego elementu. Brakującym elementem tej układanki jest RestutilService
, która jest odpowiedzialna za pobranie obrazu kodu QR z zewnętrznego interfejsu API.
W terminalu zmień bieżący katalog na usługi, wpisując ng gs restutil
i naciskając Enter. Spowoduje to utworzenie plików RestUtilService. Otwórz plik restutil.service.ts i dodaj ten fragment kodu:
private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }
private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }
Powyższa usługa pobiera obraz QR z interfejsu API innej firmy, a ponieważ odpowiedź nie jest typu JSON, ale obrazem, określamy responseType
jako 'blob'
w powyższym fragmencie.
Angular zapewnia klasę HttpClient
do komunikacji z dowolnym serwerem obsługującym HTTP. Zapewnia wiele funkcji, takich jak filtrowanie żądania przed jego uruchomieniem, odzyskiwanie odpowiedzi, umożliwianie przetwarzania odpowiedzi za pomocą wywołań zwrotnych i innych. Aby użyć tego samego, dodaj wpis dla HttpClientModule w pliku app.module.ts .
Na koniec zaimportuj tę usługę do pliku create-qr.component.ts , aby dokończyć tworzenie kodu QR.
Ale poczekaj! Wystąpił problem z powyższym tworzeniem logiki QR. Jeśli użytkownik użyje tego samego tekstu do wielokrotnego generowania kodu QR, spowoduje to połączenie sieciowe. Jednym ze sposobów rozwiązania tego problemu jest buforowanie żądania opartego na żądaniach, w ten sposób obsługując odpowiedź z pamięci podręcznej, jeśli tekst żądania jest taki sam.
Żądanie buforowania
Angular zapewnia uproszczony sposób wykonywania wywołań HTTP, HttpClient, wraz z HttpInterceptors do sprawdzania i przekształcania żądań HTTP lub odpowiedzi do iz serwerów. Może być używany do uwierzytelniania lub buforowania i wielu takich rzeczy, wiele przechwytywaczy można dodać i połączyć w łańcuch w celu dalszego przetwarzania. W tym przypadku przechwytujemy żądania i obsługujemy odpowiedź z pamięci podręcznej, jeśli tekst QR jest taki sam.
Utwórz folder przechwytywania, a następnie utwórz plik cache-interceptor.ts :
Dodaj poniższy fragment kodu do pliku:
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }
W powyższym fragmencie kodu mamy mapę z kluczem będącym adresem URL żądania, a odpowiedzią jako wartością. Sprawdzamy, czy aktualny adres URL jest obecny na mapie; if it is, then return the response (the rest is handled automatically). If the URL is not in the map, we add it.
We are not done yet. An entry to the app.module.ts is required for its proper functioning. Add the below snippet:
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { CacheInterceptor } from './interceptor/cache-interceptor'; providers: [ { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true } ],
This adds the caching feature to our application. Let's move on to the third page, the History page.
Adding The History Page
All the saved QR codes will be visible here. To create another component, open terminal type ng gc history
and press Enter.
Open history.component.css and add the below code:
.main-content { padding: 5% 10%; } .truncate { width: 90%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .center-img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; }
Open history.component.html and replace the content with this:
<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <div class="main-content"> <mat-grid-list cols="4" rowHeight="500px" *ngIf="historyList.length > 0"> <mat-grid-tile *ngFor="let qr of historyList"> <mat-card> <img mat-card-image src="{{qr.imageBase64}}"> <mat-card-content> <div class="truncate"> {{qr.text}} </div> </mat-card-content> <mat-card-actions> <button mat-button (click)="share(qr.text)">SHARE</button> <button mat-button color="accent" (click)="delete(qr.text)">DELETE</button> </mat-card-actions> </mat-card> </mat-grid-tile> </mat-grid-list> <div class="center-img" *ngIf="historyList.length == 0"> <img src="../../assets/no-see.png" width="256" height="256"> <span>Nothing to see here</span> </div> </div>
As usual, we have the header component at the top. Then, the rest of the body is a grid list that will show all the saved QR codes as individual mat-card
. For the grid view, we are using mat-grid-list
from the Angular material library. As per the drill, before we can use it, we have to first add it to the app.module.ts file.
Lista siatek mat działa jak pojemnik z wieloma elementami potomnymi kafelków o nazwie mat-grid-tile
. W powyższym fragmencie kodu HTML każdy kafelek jest tworzony przy użyciu mat-card
wykorzystując niektóre jej właściwości do ogólnego umieszczania innych elementów interfejsu użytkownika. Możemy podać number of columns
oraz rowHeight
, który służy do automatycznego obliczania szerokości. W powyższym fragmencie podajemy zarówno liczbę kolumn, jak i wartość rowHeight
.
Używamy obrazu zastępczego, gdy historia jest pusta, pobierz ją i dodaj do folderu zasobów.
Aby zaimplementować logikę wypełniania wszystkich tych informacji, otwórz plik history.component.ts i dodaj poniższy fragment do klasy HistoryComponent
:
showBackButton = true; title = 'History'; showHistoryNav = false; historyList; constructor(private storageService: StorageutilService, private snackbar: MatSnackBar ) { } ngOnInit() { this.populateHistory(); } private populateHistory() { this.historyList = this.storageService.readAllHistory(); } delete(text: string) { this.storageService.deleteHistory(text); this.populateHistory(); } share(text: string) { this.snackbar.open(text, '', {duration: 2000,}) }
Powyższa logika po prostu pobiera wszystkie zapisane kody QR i wypełnia nimi stronę. Użytkownicy mogą usunąć zapisany kod QR, co spowoduje usunięcie wpisu z pamięci lokalnej.
Więc to kończy naszą część historii... czy nie? Nadal musimy dodać mapowanie trasy dla tego komponentu. Otwórz app-routing.module.ts i dodaj mapowanie również dla strony historii:
{ path: 'history', component: HistoryComponent },
Cała tablica tras powinna teraz wyglądać tak:
const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'create', component: CreateQrComponent }, { path: 'history', component: HistoryComponent }, ];
Teraz jest dobry moment, aby uruchomić aplikację w celu sprawdzenia całego przepływu, więc otwórz terminal i wpisz ng serve
i naciśnij Enter. Następnie przejdź do localhost:4200
, aby zweryfikować działanie aplikacji.
Dodaj do GitHuba
Przed przystąpieniem do etapu wdrażania dobrze byłoby dodać projekt do repozytorium GitHub.
- Otwórz GitHuba.
- Utwórz nowe repozytorium.
- W programie VS Code użyj terminala i postępuj zgodnie z pierwszym zestawem poleceń wymienionych w przewodniku szybkiego startu, aby wypchnąć wszystkie pliki projektu.
Wystarczy odświeżyć stronę, aby sprawdzić, czy wszystkie pliki są widoczne. Od tego momentu wszelkie zmiany git (takie jak zatwierdzanie, ściąganie/wypychanie) będą odzwierciedlane w tym nowo utworzonym repozytorium.
Netlifikuj i wdrażaj
Nasza aplikacja działa na naszym lokalnym komputerze, ale aby umożliwić innym dostęp do niej, powinniśmy wdrożyć ją na platformie w chmurze i zarejestrować pod nazwą domeny. W tym miejscu do gry wkracza Netlify. Zapewnia usługi ciągłego wdrażania, integrację z GitHub i wiele innych funkcji, z których można skorzystać. W tej chwili chcemy umożliwić globalny dostęp do naszej aplikacji. Zacznijmy.
- Zarejestruj się na Netlify.
- W panelu kliknij przycisk Nowa witryna z Git .
- Kliknij GitHub na następnym ekranie.
- Autoryzuj Netlify, aby mieć dostęp do swoich repozytoriów GitHub.
- Wyszukaj i wybierz nowo utworzone repozytorium
qr
. - Netlify w kolejnym kroku pozwala nam wybrać gałąź repozytorium GitHub do wdrożeń. Zwykle używa się gałęzi
master
, ale można również mieć oddzielną gałąźrelease
, która zawiera tylko funkcje związane z wydaniem i stabilne.
Ponieważ jest to aplikacja internetowa Angular, dodaj ng build --prod
jako polecenie budowania. Opublikowane katalogi będą miały nazwę dist/qr
, jak wspomniano w pliku angular.json
.
Teraz kliknij przycisk Deploy site
, który uruchomi kompilację projektu za pomocą polecenia ng build --prod
i wygeneruje plik do dist/qr
.
Ponieważ podaliśmy informacje o ścieżce do Netlify, automatycznie pobierze on prawidłowe pliki do obsługi aplikacji internetowej. Netlify domyślnie dodaje do naszej aplikacji losową domenę.
Możesz teraz kliknąć link podany na powyższej stronie, aby uzyskać dostęp do aplikacji z dowolnego miejsca. Wreszcie aplikacja została wdrożona.
Domena niestandardowa
Na powyższym obrazku wyświetlany jest adres URL naszej aplikacji, podczas gdy subdomena jest generowana losowo. Zmieńmy to.
Kliknij przycisk Domain settings
, a następnie w sekcji Domeny niestandardowe kliknij menu z trzema kropkami i wybierz Edit site name
.
Otworzy się wyskakujące okienko, w którym można wprowadzić nową nazwę witryny; ta nazwa powinna być unikalna w całej domenie Netlify. Wprowadź dowolną dostępną nazwę witryny i kliknij Zapisz .
Teraz link do naszej aplikacji zostanie zaktualizowany o nową nazwę witryny.
Testowanie dzielone
Kolejną fajną funkcją oferowaną przez Netlify jest testowanie podzielone. Umożliwia podział ruchu, dzięki czemu różne zestawy użytkowników będą wchodzić w interakcje z różnymi wdrożeniami aplikacji. Możemy dodać nowe funkcje do innej gałęzi i podzielić ruch na wdrożenie tej gałęzi, przeanalizować ruch, a następnie połączyć gałąź funkcji z główną gałęzią wdrożenia. Skonfigurujmy to.
Warunkiem wstępnym włączenia testów dzielonych jest repozytorium GitHub z co najmniej dwiema gałęziami. Przejdź do repozytorium aplikacji w serwisie GitHub, które zostało utworzone wcześniej, i utwórz nową gałąź a
.
Repozytorium będzie miało teraz gałąź master
a
gałąź. Netlify musi być skonfigurowany do wdrażania oddziałów, więc otwórz pulpit nawigacyjny Netlify i kliknij Settings
. Po lewej stronie kliknij Build & Deploy
, następnie Continuous Deployment
, a następnie po prawej stronie w sekcji Deploy contexts
kliknij Edit settings
.
W podsekcji Branch deploys
wybierz opcję „Pozwól mi dodać poszczególne oddziały”, wpisz nazwy oddziałów i zapisz je.
Wdrażanie brances to kolejna przydatna funkcja oferowana przez Netlify; możemy wybrać, które gałęzie repozytorium GitHub mają zostać wdrożone, a także możemy włączyć podgląd dla każdego żądania pull do gałęzi master
przed scaleniem. Jest to fajna funkcja umożliwiająca programistom faktyczne przetestowanie swoich zmian na żywo przed dodaniem zmian w kodzie do głównej gałęzi wdrażania.
Teraz kliknij opcję zakładki Split Testing
u góry strony. Podzielone konfiguracje testowe zostaną przedstawione tutaj.
Możemy wybrać branżę (inną niż produkcyjną) — w tym przypadku a
. Możemy też pobawić się ustawieniami podziału ruchu. W oparciu o procent ruchu, który został przydzielony każdej gałęzi, Netlify przekieruje niektórych użytkowników do aplikacji wdrożonej przy a
gałęzi a, a innych do gałęzi master
. Po skonfigurowaniu kliknij przycisk Start test
, aby włączyć podział ruchu.
WSKAZÓWKA : Netlify może nie rozpoznać, że podłączone repozytorium GitHub ma więcej niż jedną gałąź i może wyświetlić ten błąd:
Aby rozwiązać ten problem, po prostu ponownie połącz się z repozytorium z opcji Build & Deploy
.
Netlify zapewnia również wiele innych funkcji. Właśnie omówiliśmy niektóre z jego przydatnych funkcji, aby zademonstrować łatwość konfigurowania różnych aspektów Netlify.
To doprowadza nas do końca naszej podróży. Z powodzeniem stworzyliśmy projekt Angular Material w oparciu o aplikację internetową i wdrożyliśmy go na Netlify.
Wniosek
Angular to świetny i popularny framework do tworzenia aplikacji internetowych. Dzięki oficjalnej bibliotece projektowania materiałów Angular znacznie łatwiej jest tworzyć aplikacje zgodne ze specyfikacją projektowania materiałów, co zapewnia bardzo naturalną interakcję z użytkownikami. Co więcej, aplikacja opracowana przy użyciu świetnego frameworka powinna wykorzystywać świetną platformę do wdrażania, a Netlify jest właśnie tym. Dzięki ciągłej ewolucji, świetnemu wsparciu i mnóstwu funkcji, z pewnością jest to świetna platforma do udostępniania aplikacji internetowych lub stron statycznych masom. Mamy nadzieję, że ten artykuł pomoże w rozpoczęciu nowego projektu Angular od samego pomysłu do wdrożenia.
Dalsza lektura
- Architektura kątowa
- Więcej kątowych komponentów materiałowych
- Więcej o funkcjach Netlify