4 Angular

https://github.com/angular/angular-cli/blob/master/packages/angular/cli/README.md


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

@NgModule({
declarations: [
AppComponent,
TestcComponent
],
(i zaimportowane powyżej w tym samym pliku)


Komponenty Inline
czyli zawartosc html w pliku ts komponentu

Tworzymy 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 Angular
ng 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>
.