Generating and serving an Angular project via a development server
w terminalu:
ng new PROJECT-NAME
cd PROJECT-NAME
ng serve
Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files.
Budowa projektu
W stworzonych plikach projektu plik README.md zawiera informacje o komendach
e2e - testy końcowe
node_modules - moduły potrzebne do działania
package.json - na podstawie tego pliku są zaciągane powyższe moduły
package-lock.json - faktycznie zaciągnięte moduły do node_modules
src - lokacja naszej aplikacji
src/app - stworzony startowy komponent
arc/app/app.module.ts - rdzeń naszej aplikacji
src/assets - pliki graficzne których możemy używać
src/envirinments - zawiera pliki do konfiguracji wersji produkcyjnej i developerskiej
src/index.html - uruchomienie aplikacji
src/main.ts - uruchamia naszą angularową plikację
src/style.css -
src/angular.json (wczesniej angular-cli.json) - konfiguruje naszą aplikację
tslint.json - sprawdza czy nasz kod napisany w typescrypt napisany poprawnie
tsconfig.json - określa sposób w jaki będzie używany typescrypt
arc/app/app.module.ts - rdzeń naszej aplikacji
app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } })
oznaczony @NgModule
informacje o komponentach, dyrektywach i serwisach
komponenty w pliku app.component.ts nazwane AppComponent (strona startowa)
skąd @NgModule wie o istnieniu komponentu ?
w declarations (tablica) ma zapis AppComponent (który powyżej jest importowany)
w tablicy bootstrap nazwa komponentu który ma startować aplikację (tu AppComponent)
w imports - informacja o importowanych modułach (np jeśli chcemy uzywać modułu HTTP to tu)
w providers - informacje o serwisach (w przykładowym brak)
na końcu pliku app.module.ts mamy export po to by klasa AppModule była dostępna w main.ts
main.ts
import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } platformBrowserDynamic().bootstrapModule(AppModule)
który przekazuje do uruchomienia AppModule do bootstrapModule (przedostatnia linia)
generalnie start strony zaczyna się od index.html, który ma zapis app-root:
<body> <app-root></app-root> </body>
selector app-root znajduje sie w komponencie app.component.ts
app.component.ts
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] })
dzięki czemu wie, że ma wyświetlić templateUrl: czyli app.component.html (nasz startowy html).
Czyli dzięki selektorowi z komponentu w index.html w miejscu app-root zostanie wklejony app.component.html.
Komponenty
posiada słowo kluczowe dekorator @Component
z komponentów budowana jest aplikacja Angular
komponent musi być przypisany w @NgModule (app.module.ts)
komponent jest powiązany z plikami html i css
w komponencie logika odpowiedzialna za wyswietlanie danych w widoku html
komponent posiada selector np <app-root>
Komponenty są zagnieżdżone jedne w drugich:
Budowa Komponentów
komponent jest klasą typescryptową posiadającą selektór który umożliwia wstawienie w index.htm fragmentu naszego html opisanego w komponencie (app.component.html )
zmiany dokonane w app.component.html po zapisie same odświeżają stronę
również zmiana css np dopisanie
h1 { color: red;}
po zapisie odświeży stronę
Tworzenie Komponentów
ng generate component testc
i w src/app dodaje folder w który bedzie znajdować się plik: testc.component.ts
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-testc', templateUrl: './testc.component.html', styleUrls: ['./testc.component.css'] }) export class TestcComponent implements OnInit { constructor() { } ngOnInit() {} }
w którym mamy klasę o nazwie TestcComponent, selektor app-testc i plik html testc.com...html
W selektorze mamy prefix -app , jego nazwa też moze być zmieniona
w pliku angular.json
"projects": { "angtest01": { "root": "", "sourceRoot": "src", "projectType": "application", "prefix": "app",
by selektor był unikalny i rozpoznawalny w naszej aplikacji (selektory nie moga sie powtarzac w komponentach np gdy korzystamy z zewnętrznych modułów)
plik testc.component.spec.ts to plik do testowania, nam nie potrzebny
jeśli chcemy utworzyć komponent bez tego pliku
ng generate component testc --spec false
skrót:
ng g c nazwa
generowanie bez osobnego folderu (w folderze app)
ng g c nazwa --flat true
Dodanie komponentu do projektu
znając selektor naszego dodanego komponentu (tu app-testc ) dodajemy go do naszego działającego html (u nas w app.component.html ) na końcu <app-testc></app-testc>. . . <li> <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2> </li> </ul> <app-testc></app-testc>
i w to miejsce wklei zawartość testc.component.html
zauważ, że dodał nasz nowy komponent do tablicy declarations[]: w app.module.ts
(i zaimportowane powyżej w tym samym pliku)@NgModule({ declarations: [ AppComponent, TestcComponent ],
Komponenty Inline
czyli zawartosc html w pliku ts komponentuTworzymy nowy komponent usuwamy pliki html i css, w obszarze @Component zamieniamy temaplateUrl na template i zamiast odwołania do pliku html w odwróconych ` ` wklejamy kod html.
Tak samo styleurl na style i w nawiasach i odwroconych cudzysłow [` `] wstawiamy kod css
@Component({ selector: 'app-testc2', template: `<p> testc2 works inline </p>`, styles: [`p{color: red}`] })
dopisujemy selektor do głównego pliku html w miejscu w którym ma pojawić się kod.
Komponent może służyć tylko po to by wgrać inny komponent (za pomocą selektora)
@Component({selector: 'app-testc2',template: `<app-testc></app-testc>`,})
Projekt
Tworzymy nowy projekt Angularng new PROJECT-NAME
uruchamiamy
ng serve
w główny plik html projektu app.component.html wrzucamy kod naszej tabeli, z selektorami.
<table> <tr> <td colspan="2"> <app-header></app-header> </td> </tr> <tr> <td> <app-nav></app-nav> </td> <td> <app-content></app-content> </td> </tr> <tr> <td colspan="2"> <app-footer></app-footer> </td> </tr> </table>
pozostawiamy uruchomiony projekt na pierwszym powershellu a na drugim:
Tworzymy komponenty header, nav, content, footer
ng g c header --spec false
.
.
Komponenty dodadzą się do app.modules.ts
Nasza strona powinna wyświetlać tabelę z zawartością:
Wyświetlanie danych
- interpolacja stringów - {{name}}
- dyrektywa *ngFor
- wyświetlanie warunkowe *nglf
- Interpolacja Stringów
w pliku html naszego komponentu np: app.component.html w podwójnych nawiasach możemy wstawiać, zmienne, pola klas czy nawet funkcje które coś zwracają
<p> pierwsza linia {{zmienna}}<br> Liczba pi to: {{pi}}<br> Dzis jest: {{date}} <br> moj pies to: {{dog.name}} <br> wiek: {{dog.age}}<br> {{showDog()}}<br> </p>
i wtedy w pliku app.component.ts
export class AppComponent { zmienna = 'tektury'; pi = Math.PI; date = new Date(); dog = new Dog('reksio', 4 ); showDog() { return 'Moj pies to ' + this.dog.name + ' i ma ' + this.dog.age + ' lata'; } } class Dog { constructor(public name: string, public age: number) {} }
dodajemy zmienne (zmienna, pi, date)
dodajemy nową klasę Dog (jak widać w typescrypt można stworzyć pola klasy name.age w konstruktorze )
tworzymy obiekt tej klasy (i mozemy się odwolac do jego pol dog.name i dog.age)
tworzymy w głównej klasie (AppComponent) nowa metode showDog() ktora zwraca string i w pliku html rowniez mozemy odwolać się do tej metody {{showDog() }}
Jeśli nasz obiekt nie został stworzony, tylko zadeklarowana zmienna jego typu
zamiast
dog = new Dog ();
dog: Dog;
to podczas odwoływania się do niego z poziomu html możemy się zabezpieczyć.
Dodanie znaku zapytania sprawi, że odwołanie do obiektu nastąpi tylko wtedy gdy obiekt istnieje
plik.html
moj pies to: {{dog?.name}} <br> wiek: {{dog?.age}}<br>
Użycie pipe |
https://angular.io/api/common#pipes
w pliku html mozemy za pomocą polecen formatować sposób wyświetlania np:
<p> pierwsza linia {{zmienna | uppercase}}<br> Liczba pi to: {{pi | number: '1.1-2'}}<br> Dzis jest: {{date | date: 'dd-MM-yy'}} <br> moj pies to: {{dog.name}} <br> wiek: {{dog.age}}<br> {{showDog() | lowercase}}<br> </p>
- dyrektywa *ngFor (iterowanie po kolekcjach)
dodając do pliku ts zmienna tablicowa days
days = ['pon', 'wtorek', 'sroda', 'czwart', 'piatek', 'sobota', 'niedz'];
korzystając z interpolacji stringów w html trzeba by wpisać po kolei odwołania do kolejnych pól
<p>
tytul: {{title | uppercase}}<br>
</p>
<ul> <li> {{days[0]}} </li> <li> {{days[1]}} </li> <li> {{days[2]}} </li> </ul>
żeby osiągnąć efekt:
znacznie lepiej zastosować dyrektywę *ngFor (na wzór rangeFor z c++) ="let day of days"
stworzy zmienna day która będzie inkrementować po tablicy days
<ul> <li *ngFor="let day of days"> {{day}} </li> </ul>
i wtedy możemy wpisać zmienna day w nawiasy interpolacji stringów
dyrektywa wypisze całą tablicę
Dodajemy tablicę obiektów w pliku ts, inicjalizujemy ją w konstruktorze
export class AppComponent { title = 'dni tygodnia'; days = ['pon', 'wtorek', 'sroda', 'czwart', 'piatek', 'sobota', 'niedz']; dogs = new Array<Dog>(); // tablica osbiektow constructor() { // inicjalizacja tablicy w konstruktorze this.dogs.push(new Dog('Reksio', 4), new Dog('Łatek', 3), new Dog('Maksiu', 5) ); } pies = new Dog('Reks', 8 ); // pojedynczy obiekt }
i teraz również możemy w pliku html odwołać się do tej tablicy przez *ngFor
(tu dog tylko tymczasowa zmienna do ngFor)
<ul> <li *ngFor="let dog of dogs"> imie {{dog.name }} i ma {{dog.age}} </li> </ul>
*ngFor + INDEX
Po średniku można dodać kolejną zmienna to "i" która może iterować np. po indexach.
<div *ngFor="let dog of dogs; let i = index"> {{i +1}} Imie {{dog.name }} i ma {{dog.age}} lat. </div>
Można również iterować komponenty:
Gdy mamy komponent np. testc
i jego selector (w pliku ts ) ma nazwe np 'app-testc'
testc.component.ts
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-testc', templateUrl: './testc.component.html', styleUrls: ['./testc.component.css'] })
to w naszym pliku app.component.htm taki zapis sprawi
<app-testc *ngFor ="let i of [1, 2, 3, 4]"></app-testc> ------- <app-testc2></app-testc2>
ze komponent tesc zostanie wyswietlony 4 razy (dla przykladu testc2 jeden raz )
- wyświetlanie warunkowe *nglf (uwaga dodany element <p> żeby umieścic ngFor )
jeśli warunek ma wartość true, to wyświetli
<ul> <p *ngFor="let day of days"> <li *ngIf="true"> {{day}} </li> </p> </ul>
jeśli "false" nie wyświetli
Dlatego możemy dopisać warunek czy parzyste (even, zwraca true-false)
<ul> <p *ngFor="let day of days; let e = even"> <li *ngIf="e"> {{day}} </li> </p> </ul>
Możemy sprawdzać różne warunki, np jeśli długość większa od 0 to wykonuj całość:
<div *ngIf="days.length > 0"> <ul> <p *ngFor="let day of days; let e = even"> <li *ngIf="e"> {{day}} </li> </p> </ul> </div>
Wyświetl tylko psy starsze niż 3 lata:
<div *ngFor="let dog of dogs; let i = index"> <div *ngIf="dog.age > 3"> {{i +1}} Imie {{dog.name }} i ma {{dog.age}} lat. </div> </div>
Jeśli chcemy dodawać ng.. nie w elementach HTMLowych, możemy zastosować ng-container
(co uwalnia nas od przymusowego przypisania danego obszaru do ostawień css danego elementu)
<ng-container *ngFor="let dog of dogs; let i = index"> <div *ngIf="dog.age > 3"> {{i +1}} Imie {{dog.name }} i ma {{dog.age}} lat. </div> </ng-container>
wtedy nasz warunek ngFor nie podlega żadnym stylom.
*ng Else, Then
Nasz ngIf może stosować także polecenia then i else, tu przykład wyświetlania warunkowego ng template kiedy tablica pusta.
<ng-container *ngIf="dogs.length > 0; else noDogs"> <div *ngFor="let dog of dogs; let i = index"> {{i +1}} Imie {{dog.name }} i ma {{dog.age}} lat. </div> </ng-container> <ng-template #noDogs> <p>Pusta tablica psow</p> </ng-template>
Przyciski i wywoływanie funkcji
dodajemy 2 funkcje do pliku ts
dogs = new Array<Dog>(); // tablica obiektow
addDogs() { // inicjalizacja tablicy w funkcji this.dogs.push(new Dog('Reksio', 4), new Dog('Łatek', 3), new Dog('Maksiu', 5) ); } removeDogs() { this.dogs = []; }
Rezultat po wywołaniu usunięcia tablicy (USUN)
removeDogs w pliku ts czyści tablicę, więc w html warunek ngIF gdy sprawdza wielkość tablicy uruchamia część else czyli template #noDogs
Przycisk DODAJ tworzy obiekty i ngFor z html może je wyświetlić:
*ngSwitch
konstrukcja w pliku html wygląda następująco:
<div *ngFor="let day of days"> <div [ngSwitch]="day"> <div *ngSwitchCase="'pon'">Montag</div> <div *ngSwitchCase="'wtorek'">Dienstag</div> <div *ngSwitchCase="'sroda'">Mittwoch</div> <div *ngSwitchCase="'czwart'">Donnerstag</div> <div *ngSwitchCase="'piatek'">Freitag</div> <div *ngSwitchCase="'sobotA'">Samstag</div> <div *ngSwitchDefault>Sonntak</div> </div> </div>.