Posts Angular Questions and Answers
Post
Cancel

Angular Questions and Answers

Want to replace a placeholder with some content dynamically, ie create a reusable button with the button’s text that is set dynamically?

Answer: Use <ng-content></ng-content> tag

add-btn.component

1
2
3
<button class="add-btn" (click)="add()">
    <ng-content></ng-content>
</button>

app.component

1
2
3
4
5
<div class="my-content">
    <add-btn>
        Add New Item
    </add-btn>
</div>

ng-content is used to project content into Angular components. No inputs to keep track of, no hard-coded values in the component.

Want to watch for changes when the value an input property changes?

Answer: The ngOnChanges() method is a lifecycle hook that will trigger each time Angular sets a da ta-bound input property. That means that it should be used whenever we need something to happen whenever that property value changes.

1
2
export class QuestionListComponent implements OnChanges {
    @Input() quiz: Quiz;
1
2
3
4
5
6
7
8
9
10
11
12
13
    ngOnChanges(changes: SimpleChanges) {
        if (typeof changes['quiz'] !== "undefined") {
 
            // retrieve the quiz variable change info
            var change = changes['quiz'];
 
            // only perform the task if the value has been changed
            if (!change.isFirstChange()) {
                // execute the Http request and retrieve the result
                this.loadData();
            }
        }
    }

Want to set values for properties of HTML elements or directives?

Answer: Use property binding

Binding the src property of an image:

1
<img [src]="itemImageUrl">
1
itemImageUrl = '../assets/phone.png';

Bind button disabled state to isUnchanged property

1
<button [disabled]="isUnchanged">Disabled Button</button>
1
isUnchanged = true;

Binding to a property of a directive (binding to the classes property making this blue)

1
<p [ngClass]="classes">[ngClass] binding to the classes property making this blue</p>
1
classes = 'special';
1
2
3
4
5
.special {
  background-color: #1976d2;
  color: #fff;
  padding: 1rem;
}

Model property of a custom component:

app.component

1
<app-item-detail [childItem]="parentItem"></app-item-detail>
1
parentItem = 'lamp';

item-detail.component

1
<p>Your item is:  </p>
1
@Input() childItem: string;

Result: Your item is: lamp

Want to access the DOM in Angular 10/9?

Answer: Use the ViewChild decorator combined with the ElementRef interface.

app.component

1
2
3
4
<h1>Angular 10 Example with ViewChild, AfterViewInit and ElementRef</h1>

<div #myDiv>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {

  @ViewChild("myDiv") divView: ElementRef;

  ngAfterViewInit(){

    console.log(this.divView);
    this.divView.nativeElement.innerHTML = "Hello Angular 10!";

  }

}
  1. Import the AfterViewInit, ElementRef, ViewChild APIs.
  2. Implement the AfterViewInit interface which provides our component with the ngAfterViewInit() life-cycle method that gets called after the view is intialized. We can query or modify the view only after it’s intialized.
  3. Declare the divView component property and decorate it with @ViewChild() decorator which is used to create a DOM query configuration. Here, we create a query that looks for the element with the myDiv template reference. The type of the divView variable is our ElementRef interface which means we can access the nativeElement object that reprents the DOM element in the browser.
  4. Add the ngAfterViewInit() life-cycle event and set the innerHTML of our <div> to Hello Angular 10!

This is equivalent to document.getElementById(“myDiv”).innerHTML = “Hello Angular 10!”; in plain JavaScript. Except that we use template reference variables instead of IDs in Angular.

Live example

Conclusion

ElementRef is usually combined with ViewChild and AfterViewInit to access child DOM elements from an Angular directive.

ViewChild is a custom Angular decorator for making DOM access in the Angular way. It returns the first element that matches a given component, directive or template reference selector.

Want to authenticate the requests to the RESTful web service?

Answers:

  1. The web service returns a JSON Web Token (JWT)
  2. Include the JSON web token (JWT) in subsequent HTTP requests using the Authorization HTTP header.
  3. Create a service that can be used to perform authentication and determine whether the application has been authenticated

Extending the Data Source

rest.datasource.ts RESTful data source is responsible for sending the authentication request to the /login URL and including the JWT in subsequent requests.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
import { Product } from "./product.model";
import { Cart } from "./cart.model";
import { Order } from "./order.model";
import { map } from "rxjs/operators";
import { HttpHeaders } from '@angular/common/http';

const PROTOCOL = "http";
const PORT = 3500;

@Injectable()
export class RestDataSource {
    baseUrl: string;
    auth_token: string;

constructor(private http: HttpClient) {
    this.baseUrl = `${PROTOCOL}://${location.hostname}:${PORT}/`;
}

getProducts(): Observable<Product[]> {
    return this.http.get<Product[]>(this.baseUrl + "products");
}

authenticate(user: string, pass: string): Observable<boolean> {
    return this.http.post<any>(this.baseUrl + "login", {
        name: user, password: pass
    }).pipe(map(response => {
        this.auth_token = response.success ? response.token : null;
        return response.success;
    }));
}

saveProduct(product: Product): Observable<Product> {
    return this.http.post<Product>(this.baseUrl + "products",
        product, this.getOptions());
}

updateProduct(product): Observable<Product> {
    return this.http.put<Product>(`${this.baseUrl}products/${product.id}`,
        product, this.getOptions());
}

deleteProduct(id: number): Observable<Product> {
    return this.http.delete<Product>(`${this.baseUrl}products/${id}`,
        this.getOptions());
}

getOrders(): Observable<Order[]> {
    return this.http.get<Order[]>(this.baseUrl + "orders", this.getOptions());
}

deleteOrder(id: number): Observable<Order> {
    return this.http.delete<Order>(`${this.baseUrl}orders/${id}`,
        this.getOptions());
}

updateOrder(order: Order): Observable<Order> {
return this.http.put<Order>(`${this.baseUrl}orders/${order.id}`,
order, this.getOptions());
}
private getOptions() {
    return {
        headers: new HttpHeaders({
            "Authorization": `Bearer<${this.auth_token}>`
        })
    }
}

Creating the Authentication Service

auth.service.ts is a service that can be used to perform authentication and determine whether the application has been authenticated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { RestDataSource } from "./rest.datasource";

@Injectable()
export class AuthService {

    constructor(private datasource: RestDataSource) {}

    authenticate(username: string, password: string): Observable<boolean> {
        return this.datasource.authenticate(username, password);
    }

    get authenticated(): boolean {
        return this.datasource.auth_token != null;
    }

    clear() {
        this.datasource.auth_token = null;
    }
}

Want to store and use the JSON Web Token (JWT) on the client side?

Another implementation of an authentication service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import * as moment from "moment";

@Injectable()
export class AuthService {

    constructor(private http: HttpClient) {

    }

    login(email:string, password:string ) {
        return this.http.post<User>('/api/login', {email, password})
            .do(res => this.setSession) 
            .shareReplay();
    }
          
    private setSession(authResult) {
        const expiresAt = moment().add(authResult.expiresIn,'second');

        localStorage.setItem('id_token', authResult.idToken);
        localStorage.setItem("expires_at", JSON.stringify(expiresAt.valueOf()) );
    }          

    logout() {
        localStorage.removeItem("id_token");
        localStorage.removeItem("expires_at");
    }

    public isLoggedIn() {
        return moment().isBefore(this.getExpiration());
    }

    isLoggedOut() {
        return !this.isLoggedIn();
    }

    getExpiration() {
        const expiration = localStorage.getItem("expires_at");
        const expiresAt = JSON.parse(expiration);
        return moment(expiresAt);
    }    
}

Want to send the JWT to the server on each request

  1. Use an Angular HTTP Interceptor to ensure that every request includes a JWT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    intercept(req: HttpRequest<any>,
              next: HttpHandler): Observable<HttpEvent<any>> {

        const idToken = localStorage.getItem("id_token");

        if (idToken) {
            const cloned = req.clone({
                headers: req.headers.set("Authorization",
                    "Bearer " + idToken)
            });

            return next.handle(cloned);
        }
        else {
            return next.handle(req);
        }
    }
}

Want to validate a JWT on the server side?

The BehaviorSubject class keeps track of the last event it processed and sends it to new subscribers as soon as they call the subscribe method.

This post is licensed under CC BY 4.0 by the author.