Templates - Erweitert

Structural Directives

Kurze Info zum Asterisk (*)

  • ein * signalisiert eine structural Directive
  • der Stern ersetzt eine kompliziertere Syntax durch die Kurzform
  • dies gilt für *ngIf, *ngFor und *ngSwitch
  • Ein Beispiel wird die Unterschiede zeigen

*ngIf

zum Ein- und Ausblenden von Inhalten

Entfernt das Host-Element aus dem DOM

                    
                         
                         <div *ngIf="search.length > 1">
                            
                         </div>
                    
                
                    
                         search: string = "";
                    
                

sehr nützlich als Guard für null checks

                    
                         <div *ngIf="person">
                            

{{person.firstName}}

</div>
Wichtig: Es ist ein grosser Unterschied ob ein Element mit CSS versteckt wird, oder mit *ngIf aus dem DOM entfernt wird. Beim Verstecken bleibt das Element im DOM und benötigt sowohl Speicher als auch weitere Resourcen. *ngIf ist meist die bessere Lösung.

Aufgabe 7.1 - *ngIf

  • Nutze einen Radio Button mit den Werten "Einfach" und "Erweitert"
  • Zeige bei Erweitert ein Input Feld an
  • Falls im Input Feld mehr als 2 Zeichen stehen, gebe den Text darunter aus.
  • Hint: es gibt einen Typ "radio" auf dem Input
  • Hint: Eine Radiogroup erhält man, indem man den Inputs den selben Wert für das name-Property zuweist

Aufgabe 7.1 - Mögliche Lösung

                    
                        <input type="radio" name="mode"
                               [(ngModel)]="mode" value="simple">Einfach
                        <input type="radio" name="mode"
                               [(ngModel)]="mode" value="extended">Erweitert

                        <div *ngIf="mode === 'extended'">
                            <input type="text" [value]="extendedValue"
                                   (input)="extendedValue=$event.target.value">
                            <p *ngIf="extendedValue.length >= 2">
                                Wert: {{extendedValue}}
                            </p>
                        </div>
                     
                    
                         val: string = "";
                         setting: string = "Einfach";
                    
                 

*ngIf - kurze vs. lange Syntax

                    
                         
                         <div *ngIf="search.length > 1">
                            
                         </div>
                    
                    
                        
                        <template [ngIf]="search.length > 1">
                            
</template>

*ngFor

zum Iterieren über Collections

                    
                        
    <li *ngFor="let customer of customers"> {{ customer.name}} </li>
<div *ngFor="let item of items; let idx = index;"> </div>

*ngFor - Syntax

  • eigene Angular Syntax
  • der mit *ngFor versehene Block wird wiederholt
  • Nimmt jedes Item im Items Array und weist es der Variable item zu.
  • Die sogenannte Template Input Variable ist nur im Element und dessen Kindern gültig.

Aufgabe 7.2 - *ngFor

  • Erstelle ein Array von Todos
  • Jedes Todo sollte mindestens 2 Properties (Titel, Beschreibung) haben
  • Rendere jedes Todo mit einer h2 - Überschrift für den Titel und normale Paragraphs für weitere Attribute

Aufgabe 7.2 - Mögliche Lösung

                     
                        <div *ngFor="let todo of todos">
                            <h2>{{todo.title}}</h2>
                            <p>{{todo.desc}}</p>
                        </div>
                     
                    
                            todos: any[]= [{
                                title: "*ngFor",
                                desc: "Lösung finden"
                            }, {
                                title: "*ngSwitch",
                                desc: "Folien erstellen"
                            }];
                    
                 

*ngFor Advanced - Helper Variablen

Helper boolean Variablen für odd, even, first und last und die Index-Variable index

                    
                        <div *ngFor="let todo of todos; let idx = index;
                            let first = first; let last = last;
                            let even = even; let odd = odd;">
                            {{idx}} // 0
                            {{first}} // true
                            {{last}} // true
                            {{even}} // true
                            {{odd}} // false
                        </div>
                    
                

*ngFor Advanced - Custom trackBy

Object Identity wird für Änderungen an der Liste genutzt um die Anpassungen im DOM verfügbar zu machen. Mit einer eigenen trackBy Funktion kann dies gesteuert werden. Default ist object identity ===

                    
                        <div *ngFor="let todo of todos; trackBy: myTrackFunc">
                        </div>
                    
                    
                        myTrackFunc(index, todo) {
                            return todo.id;
                        }
                    
                

*ngFor - kurze vs. lange Syntax

                    
                        <div *ngFor="let item of items; let idx = index;">
                            
                        </div>
                    
                    
                        <template ngFor let--item let-idx="index"
                            [ngForOf]="items">
                            <div>
                                
                            </div>
                        </template>
                    
                

*ngSwitch

  • [ngSwitch]="expression" ist die Direktive mit dem Switch Wert
  • *ngSwitchCase="value" ist die Direktive mit einem Case Wert, ist dieser gleich dem Switch Wert, wird das Element dem DOM hinzugefügt
  • *ngSwitchDefault fügt das Element dem DOM hinzu wenn keiner der Cases greift

Beispiel

                    
                        <div [ngSwitch]="selectedFruit">
                            <apple-view *ngSwitchCase="Fruit.APPLE">
                                </apple-view>

                            <orange-view *ngSwitchCase="Fruit.ORANGE">
                                </orange-view>

                            <no-fruit-selected *ngSwitchDefault>
                                </no-fruit-selected>
                        </div>
                    
                

ngClass

für dynamische CSS Klassen

für einzelne Klassen - ein Class Binding benutzen

                
                    

für mehrere Klassen - die ngClass Directive

                
                    <div [ngClass]="{
                      'active': customer.active,
                      'inactive': !customer.active}">
                    </div>
                
            

ngStyle

für dynamische Inline Styles

für einzelne Styles ein Style Binding benutzen

                    
                        <span
                            [style.font-weight]="pers.isVip ? 'bold' : 'normal'">
                        </span>
                    
                

für mehrere Styles die ngStyle Directive

                    
                        <span [ngStyle]="{
                          'font-size': pers.isVip ? 'bold' : 'normal'
                          'color': pers.isVip ? 'red' : 'blue'}">
                        </span>
                    
                

Inline Styles vs. Klassen

  • ich nutze NIE Inline Styles
  • Alle Styles gehören aus meiner Sicht in die Stylesheet Dateien
  • Inline Styles sind nicht wiederverwendbar
  • Inline Styles verringern die Wartbarkeit - durch das Ersetzen der Stylesheets, bspw. bei einem Redesign, sind Inline Styles davon nicht betroffen
  • CSS Klassen sind performanter als Inline-Styles

Pipes

für Transformationen von Werten

  • wird mit einer Pipe | gestartet, dann folgt die Operation, z.B. | uppercase
  • viele eingebaute Pipes, z. B. lowercase, uppercase, date, currency uvm.
  • Pipes können Parameter erhalten: {{birthday | date:"MM/dd/yy"}}
  • Eigene Pipes: PipeTransform Interface implementieren und mit @Pipe({name: 'name'}) annotieren

Beispiele

                    
                        {{birthday | date:'MM/dd/yy'}}

                        
                        {{customer | json}}

                        
                        {{kontostand | currency:'CHF':false}}

                        
                        {{titel | lowercase | uppercase}}

                        
                        {{rechnungsNr | invoiceNr}}
                    
                

Custom Pipe

                    
                        import { Pipe, PipeTransform } from '@angular/core';

                        @Pipe({name: 'ellipsis'})
                        export class EllipsisPipe implements PipeTransform {
                            transform(word: string, count: number): string {
                                if(!count) {
                                    return word;
                                }
                                if(word.length <= count) {
                                    return word
                                }
                                return word.substring(0, count) + '...';
                            }
                        }
                    
                    
                        {{'Lorem ipsum dolor' | ellipsis:10}}
                        
                    
                

Aufgabe 7.3 - Pipes

  • Erstelle eine eigene Pipe phonenumber
  • Formatiere die Nummer 0564410808 (string)
  • Die Nummer soll folgendermassen formatiert werden können
  • Default: 056 441 08 08
  • mit Parameter 41: +41 56 441 08 08

Aufgabe 7.3 - Mögliche Lösung

                    
                        import {Pipe, PipeTransform} from '@angular/core';
                        @Pipe({name: 'phonenumber'})
                        export class PhoneNumberPipe implements PipeTransform {
                          transform(phoneNumber: string, countryCode: string): string {
                            let parts = phoneNumber.match(/^(\d{3})(\d{3})(\d{2})(\d{2})$/);
                            if (!parts) return phoneNumber;
                            let formatted = `${parts[1]} ${parts[2]} ${parts[3]} ${parts[4]}`;

                            if (countryCode) {
                              formatted = `+${countryCode} ${formatted.substring(1)}`;
                            }

                            return formatted;
                          }
                        }
                    
                 

Safe Navigation Operator

Zum Vermeiden von NullPointern ;-)

  • wird mit ?. benutzt
  • Der Wert wird erst gerendert, wenn alle Properties aufgelöst werden können
  • leichtgewichtige Alternative zu chained *ngIf oder chained null checks mit &&
                
                    

Name: {{comp?.ceo?.name}}

Name: {{comp && comp.ceo && comp.ceo.name}}

Noch etwas offen?