Les 7: Varia
Les 7: Varia
Tijdens deze laatste les bekijken we hoe we een applicatie, via Firebase, kunnen publiceren als progressive web app. Daarnaast bekijken we welke Ionic en Angular lifecycle methodes er bestaan en hoe deze gebruikt kunnen worden.
Voor deze les zijn geen oefeningen voorzien.
Progressive web apps
Zoals in de inleiding besproken, is een progressive web app (PWA) een applicatie die als website aangeboden wordt. Via deze website kan de applicatie vervolgens geïnstalleerd worden op een native toestel of op een desktop/laptop computer.
Om een app aan te bieden als PWA zijn twee zaken nodig. Ten eerste moet er een service worker aangemaakt worden. Ten tweede moet een geldig web App Manifest toegevoegd worden aan de webapplicatie.
Service worker
Voor een PWA is de service worker een proxy die tussen de app en het internet staat. Een service worker heeft als taak het voorzien van een aangename en functionele offline-ervaring. Dit betekent dat de service worker elk netwerk request onderschept en één van twee mogelijke dingen doet, afhankelijk van de situatie.
De app is verbonden met het netwerk: Het HTTP request wordt afgehandeld op de klassieke manier, i.e. het wordt verstuurd naar de server. Het antwoord wordt vervolgens (door de service worker) bewaard in de cache, en doorgegeven aan de app.
De app is niet verbonden met het netwerk: Het HTTP request wordt afgebroken door de service worker en dus niet verder gestuurd naar de webserver. De service worker beantwoord het request van de applicatie met gecachete data.
Naast deze functionaliteiten, zal een service worker er ook voor zorgen dat de laatste assets gedownload worden als de applicatie verbonden is met het internet. Zo worden de laatste wijzigingen steeds weergegeven in de applicatie, zonder dat de gebruiker de app moet bijwerken.
Een service worker is niet beschikbaar op onbeveiligde verbindingen, HTTPS is dus vereist. Daarnaast is het onmogelijk om een service worker te gebruiken als de website in privénavigatie uitgevoerd wordt.
Web app manifest
Een web app manifest is een .json file die de eigenschappen van de app beschrijft. Zo zijn de naam, een beschrijving van de app, de iconen en de URL die getoond moet worden tijdens het opstarten gedefinieerd in het manifest.
PWAs & Angular
Angular voorziet, net als de andere grote frameworks, een library waarmee een Angular project omgevormd kan worden in een PWA. Deze bibliotheek kan geïnstalleerd worden via ng add. Dit commando voert, naast het installeren van pakket, ook nog een post-install script uit. Dit script zorgt voor het merendeel van de nodige configuratie in het Angular project.
Waarschuwing
In het onderstaande commando moet je ANGULAR_MAJOR_VERSION_NUMBER vervangen met de versie van het Angular package dat in je package.json staat. Als in package.json de versie 14.12.2 staat, dan gebruik je in het ng add commando het versienummer ^14.0.0.
ng add @angular/pwa@^MAJOR_ANGULAR_VERSION_NUMBER.0.0
Dit script maakt een aantal bestanden aan die hieronder besproken worden. De configuratie is gelijk voor elke applicatie, in het vervolg van de les wordt gebruik gemaakt van de oplossingen voor les 6.
ngsw-config.json
Dit bestand, dat in de root map van je project gegenereerd is, beschrijft de configuratie van de service worker. We bespreken de inhoud van dit bestand niet aangezien dit voornamelijk over optimalisaties gaat en een gedetailleerde bespreking van de opties te diepgaand is voor deze cursus. De geïnteresseerde lezer kan de verschillende opties bekijken op https://angular.io/guide/service-worker-config.

Dit bestand beschrijft hoe de service worker werkt, dus moet deze configuratie in de JavaScript app ingeladen worden. Dit gebeurt (automatisch via ng add) in app.module.ts.
import { ServiceWorkerModule } from '@angular/service-worker';
@NgModule({
declarations: [AppComponent, MenuChannelGroupComponent],
entryComponents: [],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule,
provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
provideAuth(() => getAuth()),
provideFirestore(() => {
const firestore = getFirestore();
enableMultiTabIndexedDbPersistence(firestore);
return firestore;
}),
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: environment.production,
// Register the ServiceWorker as soon as the app is stable
// or after 30 seconds (whichever comes first).
registrationStrategy: 'registerWhenStable:30000'
})
],
providers: [{provide: RouteReuseStrategy, useClass: IonicRouteStrategy}],
bootstrap: [AppComponent],
})
export class AppModule {}
manifest.webmanifest
Het web manifest wordt ook automatisch aangemaakt door het ng add commando en bevind zich in de src folder.

De standaard inhoud van dit bestand moet, zoals hieronder te zien, duidelijk aangepast worden. Daarnaast zien we ook dat het manifest automatisch gelinkt is op de index.html pagina.
Info
De bestaande iconen in het manifest worden niet overschreven door de tool die we gebruiken om de iconen te genereren. Verwijder deze defaults dus eerst.
{
"name": "app",
"short_name": "app",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable any"
},
...
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Les 6: Firebase | Voorbeeld</title>
<base href="/"/>
<meta name="color-scheme" content="light dark"/>
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta name="format-detection" content="telephone=no"/>
<meta name="msapplication-tap-highlight" content="no"/>
<link rel="icon" type="image/png" href="assets/icon/favicon.png"/>
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<link rel="manifest" href="manifest.webmanifest">
<meta name="theme-color" content="#1976d2">
</head>
<body>
<app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>
theme_color
Dit attribuut wordt gebruikt door sommige besturingssystemen en browsers om het UI aan te passen zodat dit meer overeenkomt met de PWA. Zo wordt de adresbalk in Google Chrome (Android), bijvoorbeeld aangepast.

background_color
Dit attribuut wordt gebruikt om de achtergrondkleur van de applicatie in te stellen terwijl de stylesheets aan het laden zijn. Dit wordt niet meer gebruikt nadat de PWA volledig geladen is.
name & short_name
Een PWA moet 2 namen definiëren, de eerste naam property name zal zoveel mogelijk gebruikt worden. Maar het is natuurlijk mogelijk dat deze te lang is om volledig weergegeven te worden, in dat geval wordt de short_name gebruikt.
Onderstaande video demonstreert hoe de PWA geïnstalleerd kan worden op Android, via een Chromium gebaseerde browser. Op het splash screen (rond 00:16) wordt de lange naam weergegeven, in de app-drawer (overzicht van alle apps, rond 00:14), wordt de korte naam gebruikt. Ook de theme_color (#1976d2) is duidelijk te zien in statusbalk rond (00:20).
display
Dit attribuut wordt gebruikt om te bepalen hoe de applicatie uitgevoerd wordt als PWA. Er zijn vier mogelijke opties, deze lopen van helemaal geen browser elementen tot een volledige browser ervaring.
De optie fullscreen opent de applicatie als fullscreen app en toont geen UI-elementen uit de browser. Deze optie werkt momenteel niet in chromium gebaseerde browsers, het issue staat al meer dan 3 jaar open, de kans is dus klein dat deze modus snel ondersteuning zal krijgen. Voorlopig wordt de optie standalone als fallback gebruikt.
De optie standalone kan gebruikt worden om een native app te imiteren, dit is de meestgebruikte optie. Er worden geen UI-elementen uit de browser getoond en de gebruiker kan de app minimaliseren, maximaliseren, van grootte veranderen, openen vanop de taakbalk, ... . Dit kan enkel op Windows, macOs en *nix systemen. Op Android of iOS zal de optie standalone de app natuurlijk ook openen als een fullscreen app, dit is tenslotte de manier waarop native apps getoond worden op mobiele operating systems. In onderstaande video is het resultaat van de eigenschap background_color te zien. Rond 00:15, wordt de kleur #fafafa gebruikt terwijl de app aan het laden is.
Via de optie minimal-ui heeft de app nog steeds dezelfde functionaliteiten als bij de optie standalone, er worden echter ook een minimaal aantal UI-elementen voor navigatie getoond, zoals bijvoorbeeld de "reload" en "back" knoppen.
scope
Via dit attribuut kunnen we de paden die bezocht mogen worden als de app als PWA uitgevoerd wordt beperken. De URL is relatief ten opzichte van de locatie van het manifest. Zo zou een pad als '/tabs/tab1', de gebruiker beperken tot het eerste tabblad in de app. Als de gebruiker dan navigeert naar '/', wordt de URL geopend in een webbrowser en niet langer in de PWA. Normaal gezien moet deze optie niet aangepast worden omdat de volledige applicatie als PWA gebruikt kan worden.
start_url
De URL die getoond wordt als de applicatie gestart wordt, ook deze property moet normaal gezien niet aangepast worden.
icons
De icons array bevat de verschillende icoontjes waaruit, op basis van de dpi van het toestel, het beste icoon uit gekozen wordt. Minimaal is een icoon van 192x192 en van 512x512 nodig. Op basis van deze resoluties voert een browser eventueel conversies uit om een gepast icoon te genereren. Via de tool pwa-assets-generator kunnen we uit hetzelfde icoon als voor de Android app, de nodige files genereren.
pnpm add -g pwa-asset-generator
Het onderstaand commando genereert de nodige icoontjes, en voegt deze automatisch toe aan het manifest en de index. Dit laatste is nodig voor iOS toestellen omdat Apple de Web-API Specs momenteel niet goed ondersteund. We voegen het commando eerst toe aan de package.json en roepen het vervolgens op.
{
"name": "mobile_lecture_7_example",
"version": "0.0.1",
"author": "Ionic Framework",
"homepage": "https://ionicframework.com/",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"android": "ionic build --prod && pnpm exec cap sync android && pnpm exec cap open android",
"android-resources": "capacitor-assets generate --iconBackgroundColor #eeeeee --iconBackgroundColorDark #222222 --splashBackgroundColor #eeeeee --splashBackgroundColorDark #111111 --logoSplashScale 1 --android",
"pwa-resources": "pwa-asset-generator ./resources/icon.png ./src/assets/icons -m ./src/manifest.webmanifest -i ./src/index.html",
"ios-resources": "capacitor-assets generate --iconBackgroundColor #eeeeee --iconBackgroundColorDark #222222 --splashBackgroundColor #eeeeee --splashBackgroundColorDark #111111 --ios",
"gen-resources": "pnpm run ios-resources && pnpm run android-resources",
"android-live": "ionic cap run android -l --external",
"android-run": "ionic build --prod && pnpm exec cap sync android && pnpm exec cap run android",
"android-run-default": "ionic build --prod && pnpm exec cap sync android && pnpm exec cap run android --target ADD_YOUR_OWN_DEVICE_ID_HERE"
},
.
.
.
}
pnpm run pwa-resources
App publiceren PWA via Firebase
Om de applicatie te publiceren als PWA is relatief weinig extra werk nodig, het grootste deel kan weer geautomatiseerd worden door middel van een command-line tool.
pnpm add -g firebase-tools
De eerste keer dat je deze tool gebruikt, moet je inloggen op Firebase. Dit kan via het commando
firebase login
Vervolgens kan deze tool gebruikt worden om te verbinden met een Firebase project. Het commando
firebase init
produceert onderstaande uitvoer:
PS lecture-5-firebase> firebase init
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
C:\Projects\mobileapplicationsv2\examples\mobile_lecture7_example
Before we get started, keep in mind:
* You are currently outside your home directory
Na y ingegeven te hebben, verschijnt een menu waarmee we verschillende Firebase functies kunnen activeren. Hier kies je voor hosting.
? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices.
( ) Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
( ) Firestore: Configure security rules and indexes files for Firestore
( ) Functions: Configure a Cloud Functions directory and its files
>(*) Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
( ) Hosting: Set up GitHub Action deploys
( ) Storage: Configure a security rules file for Cloud Storage
( ) Emulators: Set up local emulators for Firebase products
Vervolgens wordt er gevraagd of je een nieuw (Firebase) project wil aanmaken of een bestaand project wil gebruiken. Als je de webinterface gebruikt hebt om het project te configureren (zoals in les 6), dan kies je voor "Use an existing project", als je dit nog niet gedaan hebt, kies je voor "Create a new project".
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.
? Please select an option: (Use arrow keys)
> Use an existing project
Create a new project
Add Firebase to an existing Google Cloud Platform project
Don't set up a default project
Vervolgens wordt je gevraagd naar de public directory. Dit is de map waar het gecompileerde webproject in zit. Aangezien het commando ionic build --prod gebruikt maakt van de map www, moeten we de default optie overschrijven.
=== Hosting Setup
Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.
? What do you want to use as your public directory? (public) www
Vervolgens wordt je gevraagd of je de hosting wil configureren voor single-page applications. Dit houdt in dat als je de browser opent en een URL als https://project.web.app.com/tabs/tab1 invoert, er niet geprobeerd zal worden om een bestand https://project.web.app.com/tabs/tab1/index.html te openen. Het verzoek zal afgehandeld worden door de enige HTML-pagina in je volledige project ('https://project.web.app.com/index.html'). Kies hier dus voor y.
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) y
Vervolgens kan je automatische builds configureren die uitgevoerd worden als je een nieuwe commit pusht naar een bepaalde branch in een git repository. Dit ligt buiten de scope van de cursus, maar je bent natuurlijk vrij om zo'n pipeline op te zetten.
? Set up automatic builds and deploys with GitHub? (y/N) N
Tenslotte krijg je eventueel de vraag om een bestaande www/index.html te overschrijven. Kies hier voor N, deze index is tenslotte een gecompileerde versie van je project.
? File www/index.html already exists. Overwrite? (y/N)
Nu de configuratie afgehandeld is, kan je de applicatie publiceren en naar Firebase deployen. Om zeker te zijn dat dit de laatste versie is, maak je best nog een nieuwe build aan.
ionic build --prod
firebase deploy
Het resultaat ziet er ongeveer als volgt uit.
=== Deploying to 'les6-9b012'...
i deploying hosting
i hosting[les6-9b015]: beginning deploy...
i hosting[les6-9b015]: found 1441 files in www
+ hosting[les6-9b015]: file upload complete
i hosting[les6-9b015]: finalizing version...
+ hosting[les6-9b015]: version finalized
i hosting[les6-9b015]: releasing new version...
+ hosting[les6-9b015]: release complete
+ Deploy complete!
Project Console: https://console.firebase.google.com/project/les6-9b012/overview
Hosting URL: https://les6-9b012.web.app
De app kan vervolgens geïnstalleerd worden vanop deze URL. Hoe dit gebeurt, is hierboven gevisualiseerd bij de beschrijvingen van de verschillende mogelijkheden voor de displayopties.
Lifecycle
Ionic & Angular voorzien enkele lifecycle methodes, deze kunnen gebruikt worden om actief uit te voeren op een bepaald moment tijdens de levensduur van een component. De scroll-positie kan bijvoorbeeld aangepast worden als de view geladen is, of een formulier kan leeg gemaakt worden als de component herbruikt wordt. Daarnaast kunnen de lifecycle methodes ook gebruikt worden om subscriptions op te ruimen, zodat er geen memory leaks ontstaan en er zich geen errors voordoen.
Ionic lifecycle methodes
Ionic voorziet enkele lifecycle methodes die meerdere keren uitgevoerd kunnen worden doorheen de levensduur van een component. Af en toe herbruikt Ionic een component, in zo'n geval kunnen bugs ontstaan. Een formulier dat herbruikt wordt kan bijvoorbeeld restanten van het vorige gebruik bevatten, zoals ingevulde formuliervelden die eigenlijk leeg zouden moeten zijn.
Voor deze en andere mogelijke bugs zijn de 4 Ionic lifecycle methodes een perfecte oplossing. Deze methodes kunnen in elke component/pagina gebruikt worden zonder extra imports toe te voegen.
ionViewWillEnter
De ionViewWillEnter methode kan gebruikt worden om data te initialiseren die voor elk gebruik van de component opnieuw ingeladen of gereset moet worden, bijvoorbeeld formulierdata of een dure real-time data stream. Onderstaande video demonstreert het probleem.
Dit probleem is eenvoudig op te lossen door de instantievelden die gebonden zijn aan het formulier te resetten in de ionViewWillEnter methode.
export class NewChannelPage implements OnInit {
newChannelName = '';
isPublic = true;
users: Profile[] = [];
addedUsers = {};
userName = '';
// Niet relevante code weggelaten.
ionViewWillEnter() {
console.log('Entered new channel page, resetting view.');
this.newChannelName = '';
this.isPublic = true;
this.users = of([]);
this.addedUsers = {};
this.userName = '';
}
}
Door deze code toe te voegen is het probleem opgelost. Merk op dat in de console geprint wordt wanneer de lifecycle methode uitgevoerd wordt en dat dit duidelijk gebeurt als de view in beeld komt.
ionViewDidEnter
De methode ionViewDidEnter wordt uitgevoerd zodra de view zichtbaar is, en kan dus best gebruikt worden voor DOM-manipulatie of om elementen in de view aan te spreken via een @ViewChild() decorator. Deze methode is dan ook de eerste locatie waar een variabele die gebruik maakt van de @ViewChild() decorator niet langer undefined is.
ionViewWillLeave
Deze methode wordt uitgevoerd vlak voordat de view verdwijnt en de volgende view getoond wordt. Dit is de perfecte locatie om scroll posities, of andere van de view afhankelijke data, uit te lezen en te bewaren.
ionViewDidLeave
Deze methode wordt uitgevoerd nadat de view niet langer zichtbaar is. Hier kan je subscriptions of andere asynchrone processen stoppen die niet mogen blijven draaien op de achtergrond.
Angular lifecycle
Angular bevat een groot aantal lifecycle methodes, het merendeel wordt niet besproken tijdens deze les omdat de Ionic methodes een betere integratie bieden met de Ionic Router en RouteReuseStrategy. Angular voorziet echter 2 belangrijke methodes ngOnInit en ngOnDestroy. Daarnaast bespreken we ook de ngAfterViewChecked methode.
ngOnInit
De nOnInit lifecycle methode, wordt uitgevoerd nadat de Angular component alle data-bound properties geïnitialiseerd heeft en nadat de constructor uitgevoerd is. Dit, zoals al gezien in les 3, de ideale plaats om navigatieparameters op te halen, of iets te doen met attributen die gedefinieerd zijn met een @Input() decorator. Om gebruik te kunnen maken van deze lifecycle hook moet de component (pagina) de interface OnInit implementeren. Componenten en pagina's die aangemaakt zijn via het ionic generate commando, implementeren deze interface automatisch.
import { Component, OnInit } from '@angular/core';
export class SomePage implements OnInit {
constructor() {
// Eerst uitgevoerd, eenmaal per instantie van een component.
}
ngOnInit(): void {
// Als tweede uitgevoerd, eenmaal per instantie van een component.
}
}
ngOnDestroy
De ngOnDestroy lifecycle hook wordt uitgevoerd nadat de view uit de DOM en de pagina uit het navigation stack verwijderd is. Deze methode kan gebruikt worden om eventuele subscriptions op observables te annuleren, of om timers en intervals stop te zetten.
import { Component, OnInit, OnDestroy } from '@angular/core';
export class SomePage implements OnInit, OnDestroy {
constructor() {
// Eerst uitgevoerd, eenmaal per instantie van een component.
}
ngOnInit(): void {
// Als tweede uitgevoerd, eenmaal per instantie van een component.
}
ngOnDestroy(): void {
// Als laatste uitgevoerd, eenmaal per instantie van een component.
}
}
Om deze lifecycle methode te kunnen gebruiken, moet de OnDestroy interface geïmplementeerd worden. We passen de code van de ChannelPage component uit met een ngOnDestroy methode die, als de component verwijderd wordt, een boodschap uitprint naar de console.
export class ChannelPage implements OnInit, OnDestroy {
// Niet relevante code weggelaten.
this.channelName = '';
ngOnInit(): void {
console.log(`${this.channelName} page initialized`);
}
ngOnDestroy(): void {
console.log(`${this.channelName} page destroyed`);
}
}
Let op, het is mogelijk dat een component herbruikt wordt door Ionic, dit betekent dat de ngOnDestroy methode niet noodzakelijk uitgevoerd wordt als de component uit de view verdwijnt, maar pas als de component effectief verwijderd wordt uit de navigation stack. Dit gebeurt als onder volgende voorwaarden:
- Als de gebruiker de "back" knop indrukt (Android/Browser) of het "back-gesture" uitvoert op iOS, zal de huidige pagina van het navigation stack verwijderd worden.
- Als de gebruiker van pagina A naar B naar C naar A navigeert, dan zullen pagina's B en C verwijderd worden. Ionic zal het navigation stack doorzoeken naar een bestaande instantie van de pagina, vervolgens worden alle elementen uit het stack totdat de gewenste component (A) gevonden wordt. In onderstaand voorbeeld is A = General, B = Mobile Apps en C = React.
ngAfterViewChecked
De ngAfterViewChecked lifecycle hook wordt uitgevoerd nadat Angular gecontroleerd heeft of er in de view iets moet aangepast worden (change detection) en de nodige wijzigen gerenderd zijn. Om deze methode te gebruiken moet de AfterViewChecked interface geïmplementeerd worden.

Scroll-positie aanpassen via lifecycle methodes
Momenteel wordt de scroll-positie gereset als de gebruiker een nieuwe boodschap verstuurd, de eerste boodschap staat terug in de viewport, ook al was de gebruiker verder naar onder gescrold voordat de boodschap verstuurd werd. Onderstaande video demonstreert dit.
Via de ngAfterViewChecked hook kunnen we dit probleem oplossen, eerst passen we de ionViewDidEnter methode aan zodat deze het element dat de scroll-positie bevat ophaalt en bewaard als instantievariabele.
Via de @ViewChild() decorator kunnen we de <ion-content> component aanspreken vanuit de TypeScript code. Deze component neemt een component als argument en haalt het eerste voorkomen van deze component in de template op. De IonContent component bevat een property getScrollElement() die het HTMLElement teruggeeft dat de effectieve scroll-positie bevat. Let op, deze code moet in de ionViewDidEnter methode staan omdat deze steunt op een volledig geïnstantieerde view.
import {IonContent} from '@ionic/angular';
export class ChannelPage implements OnInit, AfterViewChecked, OnDestroy {
// Niet relevant code weggelaten.
@ViewChild(IonContent) content!: IonContent;
#scrollElement: HTMLElement;
ionViewDidEnter(): void {
this.content.getScrollElement()
.then(x => this.#scrollElement = x);
}
}
Vervolgens passen we de sendMessage methode aan zodat deze de verticale scroll-positie ophaalt voordat een bericht verstuurd wordt en deze in instantievariabele bewaard. Let op, deze caching in een instantievariabele is noodzakelijk, de scroll-positie wordt namelijk aangepast na een re-render.
export class ChannelPage implements OnInit, AfterViewChecked, OnDestroy {
// Niet relevante code weggelaten.
@ViewChild(IonContent) content: IonContent;
#scrollElement: HTMLElement;
#scrollTop = 0;
async sendMessage(): Promise<void> {
this.scrollTop = this.scrollElement?.scrollTop;
await this.dbService.sendMessage(this.channelName, this.newMessage);
this.newMessage = undefined;
}
}
Tenslotte kunnen we de ngAfterViewChecked hook gebruiken om de scroll-positie terug correct in te stellen. We gebruiken hier de scrollToPoint methode met als eerste parameter 0 omdat onze applicatie geen ondersteuning bied voor horizontaal scrollen en de horizontale scroll-positie dus steeds 0 is.
Na een time-out van 500 milliseconden scrollen we dan naar beneden zodat de nieuw boodschap zichtbaar wordt.
export class ChannelPage implements OnInit, AfterViewChecked, OnDestroy {
// Niet relevante code weggelaten.
#scrollTop = 0;
ngAfterViewChecked(): void {
if (this.content && this.#scrollElement && this.#scrollTop) {
this.content
.scrollToPoint(0, this.#scrollTop)
.then(_ => {
this.#scrollTop = undefined;
setTimeout(() => this.content.scrollToBottom(500), 500);
});
}
}
}
Als we nu een nieuw bericht versturen, zien we dat de scroll-positie onveranderd blijft en dat er automatisch naar beneden gescrold wordt.