30 martie 2020

Angular - ziua 7 - Pipes

Pipes
- transforma output
- lista de pipe-uri oferite de Angular se poate consulta aici

// Html: 
<p> {{ username | uppercase }} </p>
// alte exemple de pipes: lowercase, dateTime, date, etc.
<p> {{ user.date | date:'fullDate' }} </p>

Pipe-uri inlantuite:
<p> {{ user.date | date:'fullDate' | uppercase }} </p>

Pipe-uri personalizate:
@Pipe({
  name: 'shorten'
})
export class ShortenPipe implements PipeTransform {
  transform(value: any) {
    return value.substr(0, 10) + '...';
  }
}

// se adauga in app.module.ts -> declarations

// Html:
<p> {{ name | shorten  }} </p>

Parametrizare:
@Pipe({
  name: 'shorten'
})
export class ShortenPipe implements PipeTransform {
  transform(value: any, limit: number) { // se pot adauga mai multe filtre, ca parametri
    if (value.length > limit) {
      return value.substr(0, limit) + '...';
    }
    return value;
  }
}

// Html:
<p> {{ name | shorten:5  }} </p>

Generare sablon pentru un pipe personalizat:
> ng generate pipe

!!! Filtrul, odata aplicat, nu este rulat din nou daca se schimba datele (Array sau Object), din motive de performanta. 
Pentru a forta sa se aplica filtrul, se adauga:
@Pipe({
  name: 'shorten',
  pure: false
})

Pipe asincron
appStatus = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('stable');
  } , 2000);
});

// Html
<h2> {{ appStatus }} </h2> // va afisa [object Object], nu se uita daca obiectul se transforma dupa 2 sec

Pentru update dupa 2 secunde (merge si pentru Observables):
<h2> {{ appStatus | async }} </h2>

24 martie 2020

Angular - ziua 6 - Formulare

Formulare - 2 abordari:
- bazata pe template - Angular deduce obiectul formularului, din DOM
- reactiva - formularul este creat in mod programatic si este sincronizat cu DOM-ul

TEMPLATE

In app.module.ts, @NgModule -> imports -> FormsModule

<form (ngSubmit)="onSubmit(f)" #f="ngForm">
  <input type="text" id="username" name="username" ngModel>
</form>

In TypeScript:

onSubmit (form: NgForm) { // NgForm este un formular creat automat de Angular
     console.log(form.value['username']);
    // sau form.value.username
}

Altfel - daca vrei sa accesezi formularul nu numai la completarea lui, ci si inainte:
@ViewChild('f') signupForm: NgForm;
// ngModel aplicat pe tag-ul html iti da voie sa recuperezi obiectul in TypeScript
// onSubmit() poate deveni acum fara parametru de intrare

Cateva din alte proprietati furnizate de NgForm:
- dirty: daca formularul are vreun camp schimbat
- enabled, disabled
- valid, invalid
- touched: daca a fost facut click pe formular

Validare input:
- adaugare required in fisierul html, pe input-ul dorit
- alti validatori: email, lista completa aici => validitatea se va reflecta in atributele din NgForm
- Angular adauga dinamic clase css pe input-uri care reflecta starea, folosindu-le putem stiliza input-urile pentru a avertiza utilizatorul:

input .ng-invalid .ng-touched {
    border: 1px solid red;
}

Afisare mesaje de eroare:
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
    <input type="text" id="myemail" name="myemail" ngModel #email="ngModel">
</form>
<span class="help-block" *ngIf="!email.valid && email.touched">Please enter valid email</span>

Inserare valori initiale:
defaultQuestion='pet';

<select id='secret' [ngModel]="defaultQuestion" name="secret">
  <option value="pet">What was your first pet?</option>
  <option value="teacher">What was your first teacher?</option>
</select>


Valori predefinite in TypeScript

@ViewChild('f') myForm: NgForm;
this.myForm.setValue ({
  username: 'myChoice',
  secret: 'pet'
  // trebuiesc adaugate toate campurile
});

// suprascrie doar campurile care ne intereseaza:
this.myForm.form.patchValue({
  userData: {
    username: 'something'
  }
});

Curatare formular: this.myForm.reset();

Two-way binding:
<textarea name="questionAnswer" rows="3" [(ngModel)]="answer"/>
<p> Your reply: {{answer}} </p>
// unde answer trebuie sa fie declarat ca empty string in TypeScript


ngModelGroup - pentru a se aplica la mai mult de un singur camp

<div ngModelGroup="userData" #userData="ngModelGroup">
<!-- campuri -->
</div>
<p *ngIf="!user.Data.valid && userData.touched">User data invalid!</p>


Butoane radio
<div class="radio" *ngFor="let gender of genders">
  <label>
    <input type="radio" name="gender" [value]="gender" ngModel> {{ gender }}
  </label>
</div>

ABORDAREA REACTIVA

- nu mai este nevoie de import FormsModule, se inlocuieste cu ReactiveFormsModule

signupForm: FormGroup;

ngOnInit() {
  this.signupForm = new FormGroup({
    'username': new FormControl(null),
    'email': new FormControl(null),
    'gender': new FormControl('male')
  });
}

onSubmit() { }

// Html:
<form (ngSubmit)="onSubmit()" [formGroup]="signupForm">
    <input type="text" id="username" formControlName="username">
    <input type="text" id="email" formControlName="email">
    <input type="radio" id="gender" formControlName="gender">
</form>

Validare prin metoda reactiva:  

this.signupForm = new FormGroup({
    'username': new FormControl(null, Validators.required), // referinta la metoda de validare
    'email': new FormControl(null, [Validators.required, Validators.email]),
    'gender': new FormControl('male')
});

Afisarea validarilor
<span *ngIf="!signupForm.get('username').valid && signupForm.get('username').touched">Please enter a valid username!</span>

Grupare reactiva
- elementele se aseaza intr-un <div formGroupName="userData">
- se actualizeaza span cu noua cale catre elementul validat:
<span *ngIf="!signupForm.get('userData.username').valid && signupForm.get('userData.username').touched">Please enter a valid username!</span>


FormArray









<div formArrayMode="hobbies">
  <h4>Your hobbies</h4>
  <button type="button" (click)="onAddHobby()">Add hobby</button>
  <div class="form-group" *ngFor="let hobbyControl of getControls(); let i = index">
    <input type="text" [formControlName]="i">
  </div>
</div>

// TypeScript:
ngOnInit() {
  this.signupForm = new FormGroup({
    'username': new FormControl(null),
    'email': new FormControl(null),
    'gender': new FormControl('male'),
    'hobbies': new FormArray([])
  });
}

getControls() {
  return (<FormArray>this.signupForm.get('hobbies')).controls;
}
onAddHobby() {
  const control = new FormControl(null, Validators.required);
  (<FormArray>this.signupForm.get('hobbies')).push(control); // explicit casting
}

Creare propriii validatori

forbiddenNames(control: FormControl): {(s: string): boolean} { // format output: {'mesaj de eroare': true}
  if (this.forbiddenUsernames.indexOf(control.value) !== -1) {
    return {'name is forbidden': true};
  }
  return null; // validation is successful
}

this.signupForm = new FormGroup({
    'username': new FormControl(null, [Validators.required, this.forbiddenNames.bind(this)]),
    'email': new FormControl(null, [Validators.required, Validators.email]),
    'gender': new FormControl('male'),
    'age': new FormControl(null, Validators.pattern(/^[1-9]+[0-9]+$'/))
});
// binding-ul este pt a ne asigura ca this in metoda este ceea ce trebuie sa fie

Angular adauga erorile pe fiecare Control -> errors:
<span *ngIf="signupForm.get('userData.username').errors['name is forbidden']">This name is invalid!</span>

Validatori asincroni

forbiddenEmails(control: FormControl): Promise<any> | Observable<any> {
  const promise = new Promise<any>((resolve, reject) => {
    setTimeout(() -> {
      if (control.value === 'someAddress@yahoo.com') {
        resolve({'emailIsForbidden': true});
      } else {
        resolve(null);
      }
    }, 1500); // simulare asincronicitate
  });
  return promise;
}

// Html:
this.signupForm = new FormGroup({
    'username': new FormControl(null, [Validators.required, this.forbiddenNames.bind(this)]),
    'email': new FormControl(null, [Validators.required, Validators.email], this.forbiddenEmails.bind(this)), // al 3-lea arg este rezervat pt validatori asincroni
    'gender': new FormControl('male')
});

// TypeScript:
this.signupForm.valueChanged.subscribe(value => console.log(value));
this.signupForm.statusChanged.subscribe(status=> console.log(status)); // VALID, INVALID, PENDING (async)

setValue() si patchValue() sunt in continuare disponibile pe obiectul FormGroup.
reset() de asemenea.

19 martie 2020

Angular - ziua 5 - Observables

Observable - o sursa de date; declansat de un eveniment creat de utilizator; declansat de Http requests; poate fi declansat programatic;
--------
-- Observable --> .... stream .... -> Observer
--------
Observer-ul poate sa prelucreze datele, erorile, sau sa coordoneze terminarea, in fiecare dintre aceste actiuni se poate scrie cod (asincron) care sa fie executat.

Exemple: la un route: ActivatedRoute => route.params este un Observable, este un stream de parametri care ofera un nou parametru de fiecare data cand se schimba URL-ul
- la observabile poti doar sa faci subscribe(...)
- rolul lor cel mai important este sa emita date

Construirea unui observabil de la 0

import { interval } from 'rxjs';

// intr-o clasa:
ngOnInit() {
    interval(1000).subscribe(count => { console.log(count); } ); // primim un eveniment in fiecare secunda
}

!!! Trebuie facut unsubscribe daca observabilul nu mai este de interes (salvare Subscription intr-o variabila, si apelarea unsubscribe() in onDestroy()). Altfel, la reinitializarea componentei, se va adauga de fiecare data un nou observabil pe langa cele deja existente.
!!! Exceptie de la aceasta regula fac toti observabilii furnizati de Angular (cum ar fi route.params si altii) pentru care nu trebuie sa se apeleze unsubscribe().

Construire cu adevarat de la 0:

import { Observable } from 'rxjs';

ngOnInit() {
    const customIntervalObservable = Observable.create(observer => { 
        let count = 0;
        setInterval(() => { observer.next(count); count++; }, 1000) ;
    });

    // folosire:
    customIntervalObservable.subscribe(data => { console.log(data); });
}

// 1000 este timeout-ul
// observer.next() emite o noua valoare; se poate folosi alternativ observer.error() sau observer.complete()

Tratarea erorilor -> observer.error(new Error(' etc '));
- observabilul moare dupa ce emite o eroare (nu mai e nevoie de unsubscribe), totusi nu e acelasi lucru cu terminarea
- prindere: customIntervalObservable.subscribe(data => { console.log(data); , error => { console.error("Caught it"); } );

Terminarea unui observabil -> observer.complete();
- prindere: customIntervalObservable.subscribe(data => { console.log(data); , error => { console.error("Caught it"); }, () -> { console.log('do some cleanup'); } );
- nu este nevoie de unsubscribe()

Operatori - ajuta la construirea unor pasi prin care vrei sa obtii datele
- exemple: map, filter, expand, etc.

import { map } from 'rxjs/operators';

// in ngOnInit()
customIntervalObservable.pipe( map((data: number) => { return 'Round ' + (data + 1); }) ).subscribe(....);

customIntervalObservable.pipe( filter(...), map(...) ).subscribe(....);

Subiecti - un fel de Observable, dar sunt mai activi pentru ca se poate chema metoda next() pe ei din exterior (pe cand in observabile, next() se poate apela doar din interior)
- fiind tot niste observabile, se poate apela in continuare subscribe() si, de asemenea, unsubscribe()
- se recomanda folosirea subiectilor in detrimentul EventEmitter-ilor (mai putin cand obiectul este si un Output())
- subiectii sunt pentru comunicarea intre componente prin servicii, prin mecanisme la care se face subscribe() manual.

// in user.service.ts
import { Subject } from 'rxjs';

activatedEmitter = new Subject<boolean>(); // putea fi EventEmitter<boolean>

// Html:
<button (click)="onActivated()" />

// Ts:
onActivate() {
    this.userService.activatedEmitter.next(true); // era emit(true) pt EventEmitter
}

---
Sursa: Udemy, section 13+14

17 martie 2020

Angular - ziua 4 - Routing

Routing - functionalitate oferita de Angular pentru a da impresia mai multor pagini pe care  poate naviga un utilizator. In realitate este o singura pagina pe care se plimba mai multe componente.

Pasii pentru inregistrarea rutelor:

1. Rutele trebuie declarate in app.module.ts, exemplu:

const appRoutes = Routes [
    { path: '', component: HomeComponent },
    { path: 'users', component: UsersComponent }
]

2. In app.module.ts -> @NgModule -> imports, se adauga: RouterModule.forRoot(appRoutes)

3. In fisierul html se adauga: <router-outlet></router-outlet> in locul componentelor care fusesera adaugate folosind selectori (adica in exemplu, in locul lui <app-home> si <app-users>).

La acest moment, accesarea paginii de baza (http://localhost:4200/ si http://localhost:4200/users va duce utilizatorul catre cele 2 componente, dar nu exista navigatie in interior).

4. Adaugarea navigatiei:
- actualizare cu <a href="/"> sau <a href="/users" in html => dezavantaj ca se va face refresh la pagina de fiecare data cand se acceseaza acele link-uri
- inlocuire <a href> cu <a routerLink="/users"> pentru a scapa de refresh. Alternativa este <a [routerLink]="['/users']">

Alternativ (primii 2 pasi):
1. Outsourcing - se creeaza un nou modul "app-routing.module.ts", in care se declara const appRoutes ca la pct.1 (dar in afara  definitiei clasei). Clasa va fi adnotata cu:
@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule]
})
2. In app.module.ts -> @NgModule -> imports, se adauga AppRoutingModule

Tips<li routerLinkActive="active"> marcheaza ruta activa (via Bootstrap). Se adauga pe <li> care detine link-ul pe care s-a navigat.

Rutare programatica
Se injecteaza Router => se adauga Router in constructor, dupa care se poate folosi intr-o metoda din Typescript cu sintaxa: this.router.navigate(['/users']);

Cai relative: in constructor se injecteaza in plus route: ActivatedRoute
this.router.navigate(['/users'], {relativeTo: this.route});

Cai dinamice:
const appRoutes : Routes [
    { path: '', component: HomeComponent },
    { path: 'users', component: UsersComponent }
    { path: 'users/:id', component: UsersComponent }
]
Acum se poate accesa un anume utilizator cu id=3 la http://localhost:4200/users/3:
- id este un camp dintr-o variabila user definita in UsersComponent.
- acestei componente i se injecteaza in constructor route: ActivatedRoute
- id-ul se poate obtine apoi prin this.routes.snapshot.params['id'];
Se pot adauga si alte elemente dinamice:  { path: 'users/:id/:name', component: UsersComponent }

!!! Pentru actualizarea obiectului din Typescript cand se apeleaza un link asemanator cu rutare, pentru ca schimbarile sa se propage in template, se adauga: this.route.params.subscribe( (params: Params) -> { this.user.id = params['id']; });
!!! Pentru ca subscriptia sa nu traiasca la nesfarsit in ciuda distrugerii componentei din care face parte, se poate salva ca variabila, iar in ngOnDestroy() se va apela: this.subscription.unsubscribe();

Cai care contin parametri de tip query:
<a [routerLink]="['/users']" [queryParams]="{allowEdit: '1'}" fragment="loading" href="#"> // va duce catre http://localhost:4200/users?allowEdit=1#loading

Echivalent in Typescript: this.router.navigate(['/users'], {queryParams: {allowEdit: '1'}}, fragment: 'loading');

Accesul la parametrii de tip query (non-reactiv la schimbari):
- se injecteaza route: ActivatedRoute in constructor
- se acceseaza prin: this.route.snapshot.queryParams si this.route.snapshot.fragment
Reactiv: se apeleaza subscribe() pe cele 2 variabile, in ngOnInit().

Tips: const id = +this.route.snapshot.params['id']; // va cauza ca id-ul sa fie interpretat ca un numar si nu un string (default)

Nested routes:
const appRoutes : Routes [
    { path: '', component: HomeComponent },
    { path: 'users', component: UsersComponent, children: [
        { path: ':id', component: UsersComponent }
    ]}
]

Pastrarea parametrilor query - this.router.navigate(['/users'], {relativeTo: this.route, queryParamsHandling: 'preserve'});  // optiuni: merge, preserve

Redirectari pentru pagini invalide - se adauga in app.module.ts:

const appRoutes : Routes [
    { path: '', component: HomeComponent },
    { path: 'users', component: UsersComponent },
    { path: 'not-found', component: PageNotFoundComponent },
    { path: '**', redirectTo: '/not-found' }
]

'**' este wildcard pt orice altceva. Ordinea este importanta (redirectarea va fi intotdeauna ultima ruta adaugata)
Tips: { path: '', redirectTo: '/somewhere-else', pathMatch: 'full' } pentru ca '' sa nu mai faca match cu absolut orice

Route Guards - functionalitati care se executa inainte sa se incarce o ruta, sau imediat dupa parasirea ei.

export class AuthGuard implements CanActivate {
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
         return this.authService.isAuthenticated().then(
            (authenticated: boolean) => { if (authenticated) { return true; } else { router.navigate(['/']); } });
    }
}

Apoi,  pentru a proteja rutele:
const appRoutes : Routes [
    { path: '', component: HomeComponent },
    { path: 'users', canActivate: [AuthGuard], component: UsersComponent } // va fi accesibila doar daca canActivate() din AuthGuard intoarce true
}

Protejarea rutelor-copii: AuthGuard va implementa si interfata CanActivateChild -> se suprascrie metoda canActivateChild cu aceiasi parametri, in care doar se cheama canActivate(...)

const appRoutes : Routes [
    { path: '', component: HomeComponent },
    { path: 'users', canActivateChild: [AuthGuard], component: UsersComponent, children: [
        { path: ':id', component: UsersComponent }
    ]}
]

Transmiterea de date statice unei rute:

1. in declararea rutei se adauga:
const appRoutes : Routes [
    { path: '', component: HomeComponent },
    { path: 'users', component: UsersComponent },
    { path: 'not-found', component: PageNotFoundComponent, data: {message: 'Page not found!'} },
    { path: '**', redirectTo: '/not-found' }
]

2. in TypeScript se injecteaza un ActivatedRoute, iar in ngOnInit() se adauga: this.errorMessage = this.route.snapshot.data['message'];
Eventual se poate inlocui cu urmatoarea linie, daca vrem sa fim la curent cu schimbarile:
this.route.data.subscribe( (data: Data) => { this.errorMessage = data['message']; } );

Transmiterea de date dinamice unei rute:

1. in declararea rutei se adauga:

const appRoutes : Routes [
    { path: '', component: HomeComponent },
    { path: 'users', component: UsersComponent, resolve: {detectie: DetectionResolver} }
]
// aici detectie este un keyword ales

2. se declara DetectionResolver ca implementand Resolve<MyDataType>, in care trebuie implementata metoda resolve:

resolve (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<MyDataType> | Promise<MyDataType> | MyDataType {
    // aici se poate folosi un serviciu injectat deja in constructor pentru a putea aduce date din exterior
    // se va returna obiectul MyDataType continand informatiile dorite (sau un Observable, sau un Promise)
}

3. in TypeScript-ul componentei, in care s-a injectat un route: ActivatedRoute, se poate acum obtine obiectul MyDataType cu informatiile dorite, folosind in cazul de mai sus: this.route.snapshot.data['detectie'];

---
Sursa: cap. 11-12 Udemy

16 martie 2020

Angular - ziua 3

Directiva:
- de atribute: afecteaza un singur element, cel pe care este pus
- structurale: afecteaza mai multe elemente din DOM, contine * (exemplu *ngIf, *ngFor) - o singura directiva structurala are voie sa existe pe un element

Exemplu in TypeScript:

@Directive({
    selector: '[appBasicHighlight]' // va fi recunoscut si fara paranteze patrate, ca un atribut
})
export class BasicHighlightDirective implements OnInit{
    constructor(private elementRef: ElementRef) {}

    ngOnInit(private elementRef: ElementRef) {
        this.elementRef.nativeElement.style.backgroundColor = 'green'; // setare stil
    }
}

- se adauga in app.module.ts -> declarations, numele BasicHighlightDirective
- utilizarea in Html: <p appBasicHighlight></p>


Renderer - o abordare mai buna pentru accesul la DOM, detalii despre api-ul Renderer2 aici

@Directive({
    selector: '[appBetterHighlight]'
})
export class BetterHighlightDirective implements OnInit{
    constructor(private elementRef: ElementRef, private renderer: Renderer2) {}

    ngOnInit() {
        this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'blue');
    }
}

<p appBetterHighlight></p>


HostListener

@HostListener('mouseenter')  mouseover (eventData: Event){
     this.renderer.setStyle(...);
}
@HostListener('mouseleave')  mouseleave (eventData: Event){
     this.renderer.setStyle(...);
}

Alias-urile de la @HostListener sunt standard, specifica trigger-ul.


HostBinding - pentru scurtarea accesului la proprietati

@HostBinding('style.backgroundColor') backgroundColor: string; // alias-ul specifica proprietatea elementului (locatia din DOM)

@HostListener('mouseenter')  mouseover (eventData: Event){
     this.backgroundColor = 'blue'; // => se acceseaza mai usor stilul
}


Directiva ngSwitch

<div [ngSwitch]="value">
    <p *ngSwitchCase="5">Value is 5</p>
    <p *ngSwitchCase="10">Value is 10</p>
    <p *ngSwitchDefault>Value is default</p>
</div>


Serviciile - o clasa in Typescript, fara directive 

In componenta unde vrei sa folosesti un serviciu, se adauga tipul in @Component -> providers. Apoi serviciul trebuie injectat in constructor (nu se instantiaza): constructor(private service: MyService) {}
Instanta salvata din constructor va putea fi folosita oriunde in restul codului din acel ts.

Injectie la nivel de:
- AppModule: serviciul e vizibil in intreaga aplicatie. Se adauga in providers.
- AppComponent: serviciul e vizibil in toate componentele (dar nu in alte servicii)
- alta componenta: serviciul e vizibil in acea componenta si in toti copiii ei

@Injectable() - se adauga intr-un serviciu in care se poate injecta alt serviciu (cand te astepti sa se injecteze ceva).

13 martie 2020

Angular - ziua 2

Doua moduri de a prelua un string:
- string interpolation: <img src="{{recipe.imagePath}}"/>
- property binding: <img [src]="recipe.imagePath"/>

Scurtatura pentru un constructor clasic intr-un fisier typescript:
export class Ingredient {
    // fara declararea campurilor

    constructor(public name: string, public amount: number) {
        // fara body
    }
}

Augury: plugin de la Chrome pentru debug si analiza aplicatiei Angular

Decoratori:
- pentru clase
- pentru campuri - cu scopul de a expune proprietatile in exterior: @Input('alias') name: string;

Comunicatia intre componente:
- @Input - semnaleaza ca variabila din ts provine din afara, de la parinte. Parintele o trimite din html folosind paranteze patrate: <app-recipe-item [recipe]="recipeElem"></app-recipe-item>
- @Output - variabila va putea fi vazuta de catre parinte, in html, pe referinta catre copil, parintele o primeste in html folosind parantezele rotunde: <app-shopping-edit (ingredientAdded)="onIngredientAdded($event)"></app-shopping-edit> - aici ingredientAdded face parte din componenta-copil unde este un @Output si un EventEmitter (corelat cu $event)
!!Trimitere: paranteze patrate; Primire: paranteze rotunde !!!
- prin servicii, cand solutia de mai sus nu este eficienta
Tipul de date EventEmitter: de multe ori (sau intotdeauna?) este un @Output. In ts la o anumita actiune (onSomething()) va apela pe el insusi metoda emit().

Encapsulation: un atribut adaugat la @Component, cu 3 valori posibile:
- ViewEncapsulation.None - te scapa de atribute in plus adaugat automat elementelor - ceea ce adaugi de acum inainte in fisierul css va fi aplicat la nivel global
- ViewEncapsulation.Native - acelasi efect ca si None, dar folosind Shadow mode technology, nu toate browser-ele o suporta
- ViewEncapsulation.Emulated (default)

Local reference: in html, pe un tag, se asigneaza un fel de id la intreg tag-ul: <input type="text" #myid>
Acesta poate fi referentiat mai tarziu in html, pe evenimente: <button (click)="onAdd(myid)">
In metoda onAdd(object) din Typescript, se va primi intreg obiectul de tag.

@ViewChild@ViewChild('serverContentInput', {static: true}) serverContentInput: ElementRef;
Se adauga {static: true} doar daca se doreste accesarea elementului selectat, in ngOnInit().
Se foloseste atunci cand se face legatura intre html si ts: pe tag-ul din html se adauga "#id", iar in ts se referentiaza prin: @ViewChild('id', {static : false}) idRef: ElementRef;

@ContentChild: cand se foloseste ng-content

Metode trigger:
- ngOnInit() - apelata la initializarea unei componente, se executa dupa constructor -> clasa trebuie sa aiba implements OnInit
- ngOnChanges(changes: SimpleChanges) - cand pe o proprietate care are @Input se petrece o schimbare -> implements OnChanges
Ordinea de rulare pana acum este: constructor, ngOnChanges, ngOnInit.
- ngDoCheck() - ruleaza la orice schimbare in interiorul unei componente cand Angular trebuie sa re-randeze template-ul (proprietati, evenimente, etc). Performanta e ok totusi.
- ngAfterContentInit() - chemata dupa ce un ng-content a fost proiectat intr-un view
- ngAfterContentChecked() - chemata dupa ce un change detector a verificat un ng-content
- ngAfterViewInit() - dupa ce view-ul componentei a fost initializat, ai acces la elementele din DOM
- ngOnDestroy() - chemata inainte sa fie distrusa componenta

12 martie 2020

Angular

Un framework Javascript care manipuleaza DOM-ul. 
- pentru amestecarea de cod static si cod dinamic intr-un html
- ca filozofie, construieste componente reutilizabile

Prereq: Node

Creare aplicatie noua:
> ng new myapp

Compileaza aplicatia si o face gata sa porneasca:
> ng serve
Browser: http://localhost:4200  -> se va vedea continutul din "myapp\src\app\app.component.html"

Typescript: un framework care ofera tipuri, clase, interfete. La compilare devine un Javascript.
Npm: Node package manager (utilitar)

Bootstrap: un framework pentru stiluri css, vrem versiunea 3:
> npm install --save bootstrap@3
Dupa care se adauga la "styles" in angular.json -> "architect" -> "build",  valoarea"node_modules/bootstrap/dist/css/bootstrap.min.css".

De fapt, html-ul principal este index.html, in care apare <app-root> (un tag care va fi populat dinamic cu elemente din folderul "app", legarea se face in app.component.ts).
Ng injecteaza in pagina principala mai multe script-uri care vor executa inclusiv codul nostru.

Componente: intra ca foldere in app/
Se pot crea manual sau automat:
> ng generate component name-of-the-component

Selector
- dupa un tag nume: <nume>
- dupa numele atributului [nume]: <div nume>
- dupa numele clasei .nume: <div class="nume">
- dupa id nu este suportat

Data binding:
- de la Business logic (Typescript) la Template (Html) prin string interpolation & property binding
String interpolation: variabile definite in fisierul ts, si folosite in fisierul html, intre acolade duble  ->{{variabila}} 
Property binding: la fel, dar valoarea este luata dintre ghilimele -> "variabila"
- invers prin event binding
De exemplu, pe un buton se poate apela o metoda: (click)="onMyEvent()" , iar in implementarea din ts sa se schimbe starea unei variabile
- ambele: two way binding
Trebuie adaugat "FormsModule" la imports in app.module.ts
In forma, pe elementul pe care se face binding punem [(ngModel)]="variabila"

Directivele: sunt instructiuni in DOM, cum ar fi componentele. Exemple:
<p *ngIf="serverCreated">Server was created</p> , unde serverCreated este un boolean din fisierul ts.
Adaugand un else:
<p *ngIf="serverCreated; else noServer">
    Server was created, server name is {{serverName}}
</p>
<ng-template #noServer>
  <p>No server was created.</p>
</ng-template>

Directive ale atributelor: nu adauga sau sterg elemente, ci doar modifica elementul pe care sunt pozitionate
* NgStyle: actualizarea dinamica a stilului
<p [ngStyle]="{backgroundColor: getColor()}">Server {{serverId}} with status {{serverStatus}} </p>

* NgClass: adaugarea sau stergerea conditionata a unei clase de stil
<p [ngStyle]="{backgroundColor: getColor()}" [ngClass]="{online: serverStatus === 'Online'}">
Aici, variabila "online" este definita in fisierul ts, in @Component -> styles si se adauga la tag-ul <p> daca variabila serverStatus face egalitate cu acel string.

* NgFor: <app-server *ngFor="let server of servers"></app-server>
"servers" este definit in fisierul ts, daca sunt 2 elemente, va duplica <app-server> de 2 ori.

---
Sursa: Udemy , section 1 + 2

05 martie 2020

Judecăți de programatori

- Moartea e problemă semi-decidabilă.
Până nu mori nu ai de unde să știi că vei muri, poți avea doar așteptări rezonabile că la un moment dat se va întâmpla.
- I beg to differ. Pastrand analogia, problema este 100% decidabila (stii sigur ca algoritmul se... incheie), doar ca nu poti determina exact timpul de executie.
- Unii din oamenii care sunt acum în viață ar putea tehnic vorbind să fie nemuritori.
Nu cred că se întâmplă, dar nu ai de unde să știi până nu mor. Dacă te bazezi doar pe faptul că niciun om nu a ajuns la 200 de ani, e doar inducție partială pe mulțimea oamenilor care au murit deja, pentru că încă există 7 miliarde de cazuri care încă nu s-au oprit. Din câte știu eu, nu există o vârstă la care organismul să aibă codificat un return forțat, deci asta face problema să nu fie decidabila. Posibil să greșesc în intepretarea mea. Dacă da, unde?