Developer Blog

Tipps und Tricks für Entwickler und IT-Interessierte

Angular | Working with Angular Examples

General

On the Angular Website, there are a lot of examples.

You could download each of them and look around. In this post, i will show you to do this at once for all examples.

So, we will

  • get list of all examples
  • download zip files of examples
  • extract examples

Preparation

First, we define some variables for the Chrom Browser and the resulting output files.

# Mac OS
CHROME=/Applications/Chromium.app/Contents/MacOS/Chromium

# Windows WSL
CHROME='/mnt/c/Program Files/Google/Chrome/Application/chrome.exe'

LISTOFZIPS=listofzips

Download List of examples

Next, we will download the list of examples from https://angular.io/guide/example-apps-list.

This is not a static html file. The list will be created dynamically when viewing the page. So, only download the page with wget or curl does not help, because we need to run the JavaScript code in the browser to create the list.

Therefore, we will use Chrome in headless mode: Load the page and dump the contents of the page (dom) into a file

do_get_list() {
        "$CHROME" --headless --dump-dom https://angular.io/guide/example-apps-list | tr '>' '>\n ' >${LISTOFZIPS}.html

        echo "##. Download List of Examples"
        echo "    #Examples: ($(grep 'href="guide/' listofzips.html | wc -l))"
}

Then, we will extract all the links to the zip examples files.

A quick look into the html file shows, that the html code with the links looks like this:

We need the part in the href of the <a tag.

  • First, we will split the html file, so that every html tag will be on a separate line.
  • Then, we will cut out only the parts between apostrophes.
  • Then, we search all files containing “.zip” and the end of the line ($)
do_extract() {
        cat ${LISTOFZIPS}.html                                          |\
        tr ' ' '\n'                                                     |\
        cut -d \" -f2                                                   |\
        grep -e '.zip$'                                                 > ${LISTOFZIPS}

        echo "generated/zips/component-overview/component-overview.zip" >> ${LISTOFZIPS}

        echo "##. Extract zip files ($(wc -l ${LISTOFZIPS}))"
        echo "    #Examples: ($(wc -l ${LISTOFZIPS}))"
}

The result will be the list of URL’s for each example.

Download Examples

Next, for each of these example $ZIP, we will download the zip from the Angular Website with wget -q https://angular.io/$ZIP

do_download() {
        echo "##. Download Zips"

        (
                rm    -rf zip
                mkdir -p  zip
                cd        zip

                cat ../$LISTOFZIPS | while read ZIP
                do
                        echo "Download $ZIP"
                        if [ ! -f $ZIP ]; then
                                wget -q https://angular.io/$ZIP
                        fi
                done

                rm *.zip.1
        )
}

Extract ZIP Files

do_unzip() {
        echo "##. Unzip"

        rm    -rf src
        mkdir -p  src

        find zip -type f -exec basename {} \; | \
        while read FILE
        do
                FLDR=${FILE/.zip/}
                echo unzip zip/$FILE -d src/$FLDR
                unzip zip/$FILE -d src/$FLDR
        done
}

Upgrade all node modules to current version

We will need toe tool npm-check-updates for this.

Install it with

npm install -g npm-check-updates

ncu updates all packages to the latest version. Some Angular Examples require an earlier version of Typescript, so, after upgrade i changes the Typescript Version to 4.9.5

do_upgrade() {
	echo "##. Upgrade"

	(
		cd src
		ls | \
		while read FLDR
		do 
			echo "     $FLDR"
			(
				cd $FLDR
				ncu -u
				#     "typescript": "~4.9.3"
				sed -i '1,$s/"typescript": ".*"/"typescript": "~4.9.5"/' package.json
				# pnpm install
			)
		done
	)
}

Appendix

The code for the complete script is here:

#!/bin/bash

# Mac OS
CHROME=/Applications/Chromium.app/Contents/MacOS/Chromium

# Windows WSL
CHROME='/mnt/c/Program Files/Google/Chrome/Application/chrome.exe'

LISTOFZIPS=listofzips

do_get_list() {
        "$CHROME" --headless --dump-dom https://angular.io/guide/example-apps-list | tr '>' '>\n ' >${LISTOFZIPS}.html

        echo "##. Download List of Examples"
        echo "    #Examples: ($(grep 'href="guide/' listofzips.html | wc -l))"
}

do_extract() {
        cat ${LISTOFZIPS}.html                                          |\
        tr ' ' '\n'                                                                     |\
        cut -d \" -f2                                                           |\
        grep -e '.zip$'                                                         > ${LISTOFZIPS}

        echo "generated/zips/component-overview/component-overview.zip" >> ${LISTOFZIPS}

        echo "##. Extract zip files ($(wc -l ${LISTOFZIPS}))"
        echo "    #Examples: ($(wc -l ${LISTOFZIPS}))"
}

do_download() {
        echo "##. Download Zips"

        (
                rm    -rf zip
                mkdir -p  zip
                cd        zip

                cat ../$LISTOFZIPS | while read ZIP
                do
                        echo "Download $ZIP"
                        if [ ! -f $ZIP ]; then
                                wget -q https://angular.io/$ZIP
                        fi
                done

                rm *.zip.1
        )
}

do_unzip() {
        echo "##. Unzip"

        rm    -rf src
        mkdir -p  src

        find zip -type f -exec basename {} \; | \
        while read FILE
        do
                FLDR=${FILE/.zip/}
                echo unzip zip/$FILE -d src/$FLDR
                unzip zip/$FILE -d src/$FLDR
        done
}

do_upgrade() {
	echo "##. Upgrade"

	(
		cd src
		ls | \
		while read FLDR
		do 
			echo "     $FLDR"
			(
				cd $FLDR
				ncu -u
				#     "typescript": "~4.9.3"
				sed -i '1,$s/"typescript": ".*"/"typescript": "~4.9.5"/' package.json
				# pnpm install
			)
		done
	)
}


case "$1" in
        "get")       do_get_list;;
        "extract")   do_extract;;
        "download")  do_download;;
        "unzip")     do_unzip;;
        "upgrade")   do_upgrade;;
        *)

                echo "run $0 get|extract|download|unzip";;
esac
Angular I18N

Angular | Working with I18N

In this post, you will learn how to get started with Angular I18n using ngx-translate, the internationalization (i18n) library for Angular. We will cover the following topics:

  • setup new angular app
  • install required dependencies
  • add bootstrap as ui framework
  • create app with demo page and translation services

This will be the final result (click to show video). Source code for this post is on GitHub.

Setup new Angular app

➜ ng new app
➜ cd app

Add required modules

➜ npm install @ngx-translate/core @ngx-translate/http-loader rxjs --save

Add Bootstrap as UI framework

We need the libraries for Bootstrap and flag icons. Download the required files into your asset/vendor/bootstrap/5.3.1 folder:

Flags Icons need a CSS file with the corresponding images for the flags, so we use the archive from GitHub.

Download and extract the archive into the folder assets/vendor/flag-icons

Current folder structure

This is our current folder structure:

Setup Application

We choose the following structure for the HTML architecture.

  • index.html contains the required css and js files.
    Also, the <app-root> component, which loads our app
  • app.component.html contains the main structure of every page.
    This includes header, navigation, place for main content and footer
  • The main content is inserted via the <router-outlet>

index.html

We will add the corresponding file in the main index.html in our project.

<!doctype html>
<html lang="en" class="h-100">

<head>
  <meta charset="utf-8">
  <title>I18N</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">

  <link rel="stylesheet" href="/assets/vendor/bootstrap.min.css">
  <link rel="stylesheet" href="/assets/vendor/flag-icons.min.css">

  <link rel="stylesheet" href="/assets/vendor/bootstrap/5.1.3/css/bootstrap.min.css">
  <link rel="stylesheet" href="/assets/vendor/flag-icons/css/flag-icons.min.css">

  <link rel="stylesheet" href="/assets/css/default.css">

</head>

<body class="d-flex flex-column h-100">
  <app-root class="h-100"></app-root>

  <script src="assets/vendor/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
</body>

</html>

app.component.html

We borrow the main structure from the bootstrap example ‘Sticky Footer with Navbar‘ with some changes in the navigation bar.

<header>
    <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-primary">
        <div class="container-fluid">
            <a class="navbar-brand" href="#">
                <img src="assets/img/logo-angular.png" height="40px" width="auto">
            </a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" 
                data-bs-target="#navbarCollapse"
                aria-controls="navbarCollapse" 
                aria-expanded="false"     
                aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarCollapse">
                <ul class="navbar-nav me-auto mb-2 mb-md-0">
                    <li class="nav-item">
                        <a class="nav-link active" aria-current="page" href="#">Home</a>
                    </li>
                </ul>

                <div class="btn-group dropstart">
                    <button class="btn btn-primary dropdown-toggle" type="button" 
                        id="dropdownFlags"
                        data-bs-toggle="dropdown" aria-expanded="false">
                        <span class="flag-icon flag-icon-{{currentlang}}"></span>
                    </button>
                    <ul class="dropdown-menu">
                        <li *ngFor="let lang of languages" [value]="lang" 
                            (click)="useLanguage(lang)">
                            <a class="dropdown-item"
                                [ngClass]="{'active': currentlang == lang}">
                                <span class="flag-icon flag-icon-{{lang}}"></span>
                                &nbsp;
                                {{lang | uppercase}}
                            </a>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </nav>
</header>
<main class="flex-shrink-0">
    <div class="container">
        <router-outlet></router-outlet>
    </div>
</main>
<footer class="footer mt-auto py-3 bg-dark">
    <div class="container">
        <span class="text-muted">(C) Ralph Göstenmeier</span>
    </div>
</footer>

app.component.ts

import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

import defaultLanguage from '../assets/i18n/de.json';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss'],
})
export class AppComponent {
    title = 'I18N';

    languages = ['us', 'de', 'fr', 'sp'];
    currentlang = 'us';

    constructor(private translate: TranslateService) {
        this.currentlang = 'de';
        translate.setTranslation(this.currentlang, defaultLanguage);
        translate.setDefaultLang(this.currentlang);
    }

    ngOnInit(): void {}

    useLanguage(language: string): void {
        this.currentlang = language;
        this.translate.use(language.toLowerCase());
    }
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClient, HttpClientModule } from '@angular/common/http';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';

import { HomePageComponent } from './pages/home/component';
import { DemoPageComponent } from './pages/demo/component';

@NgModule({
    declarations: [AppComponent, HomePageComponent, DemoPageComponent],
    imports: [
        BrowserModule,
        AppRoutingModule,
        HttpClientModule,
        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: HttpLoaderFactory,
                deps: [HttpClient],
            },
        }),
    ],
    providers: [],
    bootstrap: [AppComponent],
})
export class AppModule {}

// required for AOT compilation
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
    return new TranslateHttpLoader(http);
}

How the app works

The translation is done with the ngx-translate component.

Translation works with different JSON files (for each language a separate file), containing the required translation for each text to be displayed. Each text is addressed with a name within the JSON file.

So, the base structure of each JSON file is the following:

Translation files

assets/i18n/de.json

{
  "i18n-demo-header": "I18N Demo",
  "header": "I18N Funktionalität in Angular"
}

assets/i18n/us.json

{
  "i18n-demo-header": "I18N Example",
  "header": "I18N Functionality in Angular"
}

These translations could be used in a html file by using the translate pipe:

<h1>{{'header' | translate }}</h1>

More information and examples are here.

Changing the language is done with the help of the TranslateService

Inject your app with the TranslateService (in app.component.ts)

constructor(private translate: TranslateService) {
    translate.setDefaultLang('de');
}

Change Language

useLanguage(language: string): void {
    this.translate.use(language.toLowerCase());
}

Integrate in our UI

To easy switching the language, we have to do a few steps

Add a dropdown menu to our navigation bar.

And switching the language is done by calling useLanguage within each menu item:

<div class="btn-group dropstart">
    <button class="btn btn-primary dropdown-toggle" type="button"
        id="dropdownFlags"
        data-bs-toggle="dropdown" aria-expanded="false"><span
        class="flag-icon flag-icon-{{currentlang}}"></span>
    </button>
    <ul class="dropdown-menu">
        <li *ngFor="let lang of languages" [value]="lang"
            (click)="useLanguage(lang)">
            <a class="dropdown-item" 
                [ngClass]="{'active': currentlang == lang}">
                <span class="flag-icon flag-icon-{{lang}}"></span>
                &nbsp;
                {{lang | uppercase}}
            </a>
        </li>
    </ul>
</div>

Setup a list for all menu items:

<ul class="dropdown-menu">
<li *ngFor="let lang of languages" [value]="lang" (click)="useLanguage(lang)">

Angular | Getting Started with matter.js

Matter.js is a 2D rigid body physics engine for the web written in JavaScript.

Creating an angular app with pages using matter.js is easy. Complete code is here.

For a more details post, please take a look at here: Angular | Working with matter.js

Create starter app

First, create a default angular app with ng new.

❯ ng new app-starter
? Do you want to enforce stricter type checking and stricter bundle budgets in the workspace?
  This setting helps improve maintainability and catch bugs ahead of time.
  For more information, see https://angular.io/strict Yes
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS
cd app-starter

Add matter.js

Matter.js is a javascript library, so we have to add the corresponding files to our angular app. We will use the npm module matter-js.

npm install matter-js

We also add the type definitions, so that Visual Studio code knows how to check our code.

npm install @types/matter-js --save-dev 

Clear content of app.component.html

To keep out demopage clean, we will remove unnecessary code from app.component.html.

Remove all of the content and just keep the last line

<router-outlet></router-outlet>

Create pager for MatterJS democode

We will use a seperate Angular component/page for our matter.js demo

 ng generate component pages/Demo

app-routing.module.ts

Next, we add a routing for our demopage.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DemoComponent } from './pages/demo/demo.component';

const routes: Routes = [
  { path: 'demo', component: DemoComponent },
  { path: '',   redirectTo: '/demo', pathMatch: 'full' },
  { path: '**', redirectTo: '/demo', pathMatch: 'full' },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Start App

Ready to take a first look at our app.

yarn start

demo.component.scss

Now, we do a little styling

.demo {
    background-color: lightgray;
    border: 4 dotted black;
}

demo.component.ts

And finally the matter.js code. Starting with the default imports for a angular app

import { Component, OnInit } from '@angular/core'

Next, we import the required matter.js parts

import { Engine, Runner, Render, World, Constraint, MouseConstraint, Bodies} from 'matter-js'

Following the definition for our DemoComponent

@Component({
	selector: 'app-minimal',
	templateUrl: './demo.component.html',
	styleUrls: ['./demo.component.scss'],
})
export class DemoComponent implements OnInit {
	constructor() {}

	ngOnInit() {
		this.demo()
	}

	demo() {
	}
}

The main code of out demo will be in the function demo()

	demo() {
		var engine = Engine.create()
		var render = Render.create({
			element: document.body,
			engine: engine,
			options: {
				width: 800,
				height: 400,
				wireframes: false,
			},
		})

		var boxA = Bodies.rectangle(400, 200, 80, 80)
		var ballA = Bodies.circle(380, 100, 40, {})
		var ballB = Bodies.circle(460, 10, 40, {})
		var ground = Bodies.rectangle(400, 380, 810, 60, {
			isStatic: true,
		})

		World.add(engine.world, [boxA, ballA, ballB, ground])

		Engine.run(engine)
		Render.run(render)
	}
}

Angular | Working with State Management

Introduction

One of the most challenging things in software development is state management.

Motivation

What is a state (or application state)? Theoretically, it is the entire memory of the application, but, typically, it is the data received via API calls, user inputs, presentation UI State, app preferences, etc.

It is the data that can differentiate two instances of the same application.

One example of application state would be a list of customers or products maintained in an application.

Problem (we’re trying to solve)

Think of an application using a list of data (products, customers, …). This list is the state that we are trying to manage.

Some API calls and user inputs could change the state ( i.e. the list ) by adding or removing items/entries. The state change should be reflected in the UI and other dependent components.

We could solve this with a global variable to hold the list and then add/remove customers from/to it and then write the code to update the UI and dependencies. But, there are many pitfalls in that design which are not the focus of this article.

Solutions

Currently there are several state management libraries for Angular apps: NGRX, NGXS or Akita.

RxJS – Reactive Extensions Library for JavaScript

RxJS is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code. This project is a rewrite of Reactive-Extensions/RxJS with better performance, better modularity, better debuggable call stacks, while staying mostly backwards compatible, with some breaking changes that reduce the API surface

Vorschau(öffnet in neuem Tab)

@ngrx/store

Store is RxJS powered state management for Angular applications, inspired by Redux. Store is a controlled state container designed to help write performant, consistent applications on top of Angular.

Key concepts

  • Actions describe unique events that are dispatched from components and services.
  • State changes are handled by pure functions called reducers that take the current state and the latest action to compute a new state.
  • Selectors are pure functions used to select, derive and compose pieces of state.
  • State is accessed with the Store, an observable of state and an observer of actions
NgRx State Management Lifecycle Diagram

Akita

Akita is a state management pattern, built on top of RxJS, which takes the idea of multiple data stores from Flux and the immutable updates from Redux, along with the concept of streaming data, to create the Observable Data Stores model.

Akita encourages simplicity. It saves you the hassle of creating boilerplate code and offers powerful tools with a moderate learning curve, suitable for both experienced and inexperienced developers alike.

Akita is based on object-oriented design principles instead of functional programming, so developers with OOP experience should feel right at home. Its opinionated structure provides your team with a fixed pattern that cannot be deviated from.

NGXS – State management pattern + library for Angular

NGXS is a state management pattern + library for Angular. It acts as a single source of truth for your application’s state, providing simple rules for predictable state mutations.

NGXS is modeled after the CQRS pattern popularly implemented in libraries like Redux and NgRx but reduces boilerplate by using modern TypeScript features such as classes and decorators.

Introduction (taken from here)

What is state?

State is basically everything that will define the UI that our user will be using. State could be whether a button should be visible or not, it could be the result of that button click and it could also be an Array of users that is coming from an API. State can live in different places throughout our entire application. Some state is very specific to a certain component where other state might be shared in different parts of our application. One piece of state could be a singleton instance, where a another piece of state could share the limited lifespan of a component that can be destroyed at any time.

This big variety of what state could be, how long it lives and where it comes from results in complexity that we need to manage.

What is state management?

State management is the concept of adding, updating, removing and reading pieces of state in an application. When we have deeply nested data structures and we want to update a specific part deep down in the tree, it might become complex. In that case we have state management libraries that contain a Store which helps us with state management to get rid of that complexity. A quick note, we have to be careful that these libraries don’t add complexity by overusing them.

Reactive state

Combining state management together with reactive programming can be a really nice way to develop single-page-applications. Whether our focus lies on Angular, Vue or React, combining these two principles will result in more predictable applications.

Now what has state to do with reactive programming? A piece of state can change over time, so in a way we are waiting for new state changes. That makes it asynchronous.

Let’s take this example for instance:

// false------true-----false---true...
sidebarCollapsed$ = this.state.sidebarCollapsed$

The sidebarCollapsed$ stream starts out with false, later on it becomes true and so on. This stream keeps on living. In Angular this state can be consumed with the async pipe as easy as:

<my-awesome-sidebar *ngIf="sidebarCollapsed$|async">
</my-awesome-sidebar>

The async pipe will subscribe to the sidebarCollapsed$ pass it to the component, mark it for check and will automatically unsubscribe when the component gets destroyed. Keeping state in an observer pattern is nice because we can subscribe to the changes. Oh, and did I mention it plays super nice with Angular?

We can either use a BehaviorSubject or state management frameworks that support Observables. Here are some really great ones with Observable support:

Immutability and Unidirectional data flow

Before we dive deeper in state, there are 2 important principles that we should follow when managing state. The first principle is immutability, which means that we should never mutate data directly without creating a new reference of that object. If we mutate data directly, our application becomes unpredictable and it’s really hard to trace bugs. When we work in an immutable fashion we can also take advantage of performance strategies like the ChangeDetection.OnPush from Angular or React its PureComponent.

When we use typescript we can enforce the typescript compiler to complain when we mutate data

type Foo = {
    readonly bar: string; 
    readonly baz: number; 
}

let first = {bar: 'test', baz: 1};
first.bar = 'test2'; // compilation error
first = {...first, bar: 'test2'}; // success

In the previous example we have overwritten the first instance with an entire new instance that has an updated bar property.

Arrays can be handled like this:

let arr = ['Brecht', 'Kwinten'];
arr.push('John'); // BAD: arr is mutated
arr = [...arr, 'John']; // Good, arr gets new reference

the Array prototype also has some great helper functions that we can use to enforce immutability like map() and filter() but this is not in scope for this article.

The second principle is Unidirectional data flow. In a nutshell, this means that we should never use two-way data binding on state. It is the absolute owner of that specific piece of state that is in charge of updating it (immutable of course).

Both of these principles are highly enforced by the Redux pattern.

What kind of states are there?

Router state

Often forgotten, but one of the most important pieces of state a web application can have. Putting state in the route gives us the following advantages:

  • We can use the browser navigation buttons
  • We can bookmark the state
  • We can can copy and paste the url with the state to other users
  • We don’t have to manage it, it’s always there in the route

Tip: Instead of handling modals with a userDetailModalVisible property, why not enjoy all the benefits mentioned above and bind it to a users/:userId route? Using a child router-outlet in Angular makes this a piece of cake as we can see in this snippet.

<table>
<!--contains users -->
</table>
<router-outlet>
<!-- user detail modal rendered in here -->
</router-outlet>

Component state

Every component could contain state. That state could be shared with its dumb components or could be used in the component itself. Eg: When an ItemComponent has a property selectedItems which is an array of ids, and that array is never used in other components (that aren’t children of that component), we can consider it component state. It belongs to that component, therefore the component should be responsible for it. Child components can consume that state but should never mutate it. Those components can notify their parent that is responsible for it, which could update it in an immutable way. For more information about smart and dumb components look here.

Personally, I try to avoid state management frameworks for managing component state because it’s the responsibility of that component to manage that state. There are however good reasons to use state management frameworks to manage component state:

If the state management of the component becomes a bit too complex and we don’t want to use a state management framework just yet, we could use a state reducer in the component itself.

Persisted state

Persisted state, is state that is being remembered when the user navigates between different pages. This could be whether a sidebar was collapsed or not, or when the user returns to a grid with a lot of filters and he wants them to be remembered and reapplied when he returns. Another example is a wizard with different steps, and every step needs to be persisted so the user can navigate back and forth and the last page is a result of all these steps.

Persisted state is the type of state where we typically use a state management framework for, that being said, if we don’t want to rely on an external dependency we can also manage it in a Angular service which can be a singleton that is shared throughout the entire application. If that service becomes too complex or there is a lot of state to manage, I would consider to put that state into a state management framework.

Shared state

When we are talking about shared state, we are talking about state that needs to be shared between different parts of our application. State that is being shared throughout different smart components. This means that the instance of this piece of state should live on a higher level, than the components that want to consume it.

Shared state can be managed in a state management framework like ReduxNgrxAkitaNgxs and so on, but if that state is small and simple we can also manage it manually. Let’s say that we want an Observable of an Array of countries that we need to share throughout the entire application. In Angular we could have a CountryService that fetches the countries from the API once, and then shares it throughout the entire application. For that we can use the shareReplay operator from RxJS.

export class CountryService {
    ...
    countries$ = this.httpClient.get('countries').pipe(shareReplay(1));
}

Simple right, one line of code?! For this we don’t need a state management framework, although it can also have its benefits. Some developers like to keep all their master data in a Redux store, and that’s fine. Just know that we don’t have to. I like to develop by the KISS principle (Keep ISimple Stupid) as much as possible, so I favor this approach many times. Think about the amount of lines of code we saved by this approach. Beware that every line of code we write, not only needs to be written but also maintained.

Which state needs to be managed?

Now that we know what state is, we have to ask ourselves which state needs to be managed, and where do we manage that state? In a component, singleton service or a framework (Store)?

This is the part where the strong opinions surface. I would suggest to use what works for you and your team and really think about, but here are my personal opinionated guidelines:

  • I try to avoid state management frameworks where possible. RxJS already leverages us with a lot already and I like to think KISS.
  • I try to avoid using state management frameworks to communicate with different parts in my application, I believe state is unrelated to communication.
  • When my component can handle the state and it’s not too complex, I let my component in charge of managing that state.
  • Master data like countries are exposed in a service which uses the shareReplay operator.
  • I don’t put the result of a getById API call into a store if there is no one consuming that state except for the component requesting it
  • I use a facade between my smart components and my store/services to make refactoring easier in the future.

However, there is also a popular opinion out there to put literally everything in the store which has the following advantages:

  • We can see the flow of the code in devtools
  • Consistent pattern
  • We can leverage selectors with memoization
  • Easier for realtime applications
  • Optimistic updates are easier

However, there are a few downsides as well:

  • A gigantic amount of bloat code: Bigger bundle size, more maintenance and dev time. Eg: If we would use the complete Ngrx pattern for the countries$ example we would have to write an: actionactiontypeeffect and a reducer.
  • Tightly coupled to a strong dependency that is hard to get rid of in the future
  • Generally more complex
  • The user his screen can get out of sync with the backend
  • Cache invalidation: if we add a currentUserToEdit in the store, we have to get it out when we navigate away
  • We can’t use the async pipe to cancel pending XHR requests
  • We create a distributed monolith of some sort

More to read

Ionic | Working with p5.js

Introduction

p5.js is a JavaScript library for creative coding, with a focus on making coding accessible and inclusive for artists, designers, educators, beginners, and anyone else.

In this Post, you will learn how to create an ionic app using p5.js to create an amazing demo of the Lissajous Curve (de/en).

If you want to learn more about this amazing graphs, take a look at some examples at codepen.io. Some amazing examples are here, here and here.

As always, you will find the final project on Github.

Preparation

You will need to install Node.Js, Ionic and p5.js.

First step is, to install Node.Js. I will refer to the installation pages on Node JS.

After installing Node.js, you will also have the Node Package manager, npm. We will use this tool to install the other requirements.

Install Ionic

$ npm -g install ionic

Create base app structure

$ ionic start working-with-p5js blank --type angular
$ cd working-with-p5js

Add p5.js to the ionic app

$ npm install --save-dev p5

Test your base app

$ ionic serve

Starting with p5.js

Add the base components of p5.js to our app.

Modify home.page.html

<ion-header>
	<ion-toolbar>
		<ion-title>Working with p5.js</ion-title>
	</ion-toolbar>
</ion-header>

<ion-content>
	<div id="canvasContainer" class="canvas-container"></div>
</ion-content>

Modify home.page.ts

import { Component, OnInit, ElementRef } from '@angular/core';

import * as p5 from 'p5';

@Component({
	selector: 'app-home',
	templateUrl: 'home.page.html',
	styleUrls: [ 'home.page.scss' ],
})
export class HomePage implements OnInit {

  curve: any;
  canvasSizeX = 200;
  canvasSizeY = 200;

  constructor(private el: ElementRef) { }

  ngOnInit() {
    const p5obj = new p5(p => {
      p.setup = () => { this.setup(p); };
      p.draw = () => { this.draw(p); };
    }, this.el.nativeElement);
  }

  setup(p) {
    const c = document.querySelector('#canvasContainer');
    p
      .createCanvas(this.canvasSizeX, this.canvasSizeY)
      .parent(c);
  }

  draw(p) {
    p.background(220);
  }
}

And the result ist your frist p5.js graphic:

Next, we will grab an example from p5.js: form-regular-polygon

Modify home.page.ts

We modify the draw function and add a required polygon function.

import { Component, OnInit, ElementRef } from '@angular/core';

import * as p5 from 'p5';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {

  curve: any;
  canvasSizeX = 720;
  canvasSizeY = 400;

  private ID = 'HomePage';
  log(func, line = '') {
    console.log(this.ID + '::' + func + '|' + line);
  }

  constructor(
    private el: ElementRef
  ) {
    this.log('constructor');
  }

  ngOnInit() {
    this.log('ngOnInit');

    const p5obj = new p5(p => {
      p.setup = () => {
        this.setup(p);
      };

      p.draw = () => {
        this.draw(p);
      };
    }, this.el.nativeElement);
  }

  setup(p) {
    this.log('setup');

    const c = document.querySelector('#canvasContainer');
    p
      .createCanvas(this.canvasSizeX, this.canvasSizeY)
      .parent(c);
  }

  polygon(p, x, y, radius, npoints, color) {
    const angle = p.TWO_PI / npoints;
    p.beginShape();

    p.fill(color);
    for (let a = 0; a < p.TWO_PI; a += angle) {
      const sx = x + Math.cos(a) * radius;
      const sy = y + Math.sin(a) * radius;
      p.vertex(sx, sy);
    }
    p.endShape(p.CLOSE);
  }

  draw_figure(p, scaleX, scaleY, divisor, radius, npoints, color) {
    p.push();
    p.translate(this.canvasSizeX * scaleX, this.canvasSizeY * scaleY);
    p.rotate(p.frameCount / divisor);
    this.polygon(p, 0, 0, radius, npoints, color);
    p.pop();
  }

  draw(p) {
    p.
      background('white');

    this.draw_figure(p, 0.2, 0.5, 200.0, 82, 3, 'red');
    this.draw_figure(p, 0.5, 0.5, 50.0, 80, 20, 'blue');
    this.draw_figure(p, 0.8, 0.5, -100.0, 70, 7, 'green');
  }
}

And we get a nice litte animation:

Additional Resources and Reading

Websites

rxjs

rxjs | Cookbook

Informations

NGRX – Reactive State for Angular

Original Post is here. Github repo has the code snippets at choopage’s GitHub repo.

Recipes


Chaining of observable

The below snippet would return 0, 1, 2, 3…, n until it is stopped. It would be returned at every 2 sec. See reference here.

import { Observable } <strong>from 'rxjs/Rx';

let obs = Observable.<em>interval</em>(2000);
let req = obs
    .flatMap(v => { return Observable.of(v) })
    .subscribe(
        v   => console.log(v),
        err => console.error(err),
        ()  => console.log(<strong>'done'</strong>)
    );

Response transformer

The below would return undefined at every 2 sec interval.

import { Observable } from 'rxjs/Rx';

let obs = Observable.interval(2000);

obs
    .let(responseTransformer)
    .subscribe(
        v  => console.log(v),
        err=> console.error(err)
    );

responseTransformer(input: Observable<any>) {
    return input.map(v => v.data);
}

Using RxJS composition over base class extension

export class MyAPI {
    constructor(private http: Http) {
    }

    get(url: string, options: any) {
        return this.http.get(url, options).let(responseTransformer);
    }
}

responseTransformer(input: Observable<any>) {
    return input.map(v => v.data);
}

Higher Order Observable

We create higher order observable using .map.

const numObservable = Rx.Observable.interval(1000).take(4);
const higherOrderObservable = numObservable.map(x => Rx.Observable.of(1, 2));

higherOrderObservable.subscribe(<br>x => x.subscribe(y => console.log(y)));

Further use of higher order observable

usingHigherOrderObservable() {
    Observable
        .interval(1000)
        .groupBy(n => n
}

Flatten a higher order observable with RxJS switch

const numObservable = Rx.Observable.interval(1000).take(2);
const higherOrderObservable = numObservable.map(x => Rx.Observable.of(1, 2)).switch();

/* 
------+--------+---------
       \        \
        1,2      1,2
        
        switch
        
------1-2-------1-2-------

Switch map flattens the higher order observable
*/

higherOrderObservable.subscribe(x => console.log(x));

switchMap: map and flatten higher order observables

const clickObservable = Rx.Observable.fromEvent(document, 'click');

function performRequest() {
  return fetch('<a href="http://jsonplaceholder.typicode.com/users/1'" target="_blank" rel="noreferrer noopener">http://jsonplaceholder.typicode.com/users/1'</a>)
  .then(res =>; res.json());
  //this returns a Promise
}
//Observabl<Event> --> Observable<Response><br>const responseObservable = clickObservable<br>  .switchMap(click => performRequest());//switchMap can convert Promise to Observable<br>//switchMap = map .... + ... switchresponseObservable.subscribe(x => console.log(x.email));

Use groupBy in real RxJS applications

See reference here.

const busObservable = Rx.Observable.of(
  {code: 'en-us', value: '-TEST-'},
  {code: 'en-us', value: 'hello'},
  {code: 'es', value: '-TEST-'},
  {code: 'en-us', value: 'amazing'},
  {code: 'pt-br', value: '-TEST-'},
  {code: 'pt-br', value: 'olá'},
  {code: 'es', value: 'hola'},
  {code: 'es', value: 'mundo'},
  {code: 'en-us', value: 'world'},
  {code: 'pt-br', value: 'mundo'},
  {code: 'es', value: 'asombroso'},
  {code: 'pt-br', value: 'maravilhoso'}
).concatMap(x => Rx.Observable.of(x).delay(500));const all = busObservable
  .groupBy(obj => obj.code);
  .mergeMap(innerObs => innerObs.skip(1).map(obj => obj.value));//Alternatively could using filter and map
/*
const enUS = busObservable
  .filter(obj => obj.code === 'en-us')
  .map(obj => obj.value);const es = busObservable
  .filter(obj => obj.code === 'es')
  .map(obj => obj.value);const all = Rx.Observable.merge(enUS, es);
*/all.subscribe(x => console.log(x));

Using .map versus .switchMap

The below code snippet we can view the result of using .map versus .switchMap

//user.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from "rxjs";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';

@Injectable()
export class UserService {

  constructor(private http: Http) { }

  getUsers(): Observable<any> {
    return this.http.get('http://jsonplaceholder.typicode.com/users')
        //.map(v => v.json());
        .switchMap(v => v.json());
  }

}
//app.component.ts
import { Component } from '@angular/core';
import { UserService } from "./user.service";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';

  constructor(private userService: UserService) {

  }

  search(term: string) {
    this.userService.getUsers()
        .subscribe(v => console.log(v));

    /*
    //we can do this with .switchMap
    this.userService.getUsers()
        .subscribe(v => {if (v.email != "Sincere@april.biz") {
          console.log(v.email);
        }});
     */
  }
}

Solving the multiple Async Pipe in Angular ≥ 2.0.0 with share operator

Remember to import import "rxjs/add/operator/share"; See reference here.

squareData$: Observable<string> = Observable.range(0, 10)
        .map(x => x * x)
        .do(x => console.log(`CalculationResult: ${x}`)
        .toArray()
        .map(squares => squares.join(", "))
        .share();  // remove this line: console will log every result 3 times instead of 1

Managing Cold and Hot Observables using publish().refCount() which is similar to .share()

ngOnInit() {
    // in angular 2 and above component.ts file add these    this.coldObservable();
    this.hotObservable();
}
/*
* cold observable is like a recast of video
* */
coldObservable() {
    let incrementalObs = Observable.interval(1000).take(10).map(x => x + 1);
    incrementalObs.subscribe(val => console.log('a: ' + val));
    setTimeout(function() {
        incrementalObs.subscribe(val => console.log('      b: ' + val));
    }, 4500);
}


/*
* hot observable is like watching a live video
* */
hotObservable() {
    let incrementalObs = Observable.interval(1000).take(10).map(x => x + 1).publish().refCount(); //can also use .share()
    incrementalObs.subscribe(val => console.log('a: ' + val));
    setTimeout(function() {
        incrementalObs.subscribe(val => console.log('      b: ' + val));
    }, 4500);
}

Observables Array Operations with flatMap

Reference Rangle.io article.

getLoadList(): void {
  this.shareService
      .fetchLoad()
      .take(1)
      .filter(response => {
        if ( response.status === 200 ) {
          return true;
        } else if ( response.status === 304 ) {
          // do something more
          return false;
        } else {
          this.gotoErrorPage();
          return false;
        }
      })
      .flatMap(response => response.data.loads as Load[])
      .filter(obj => obj.content.contentGrade === 'x')
      .subscribe(
          val => console.log(val),
          err => {
            console.error(err);
          });
}

Error Handling


Error handling in RxJS

Some learning points from RxJS lesson videos. This repo is available in my GitHub repo.

import { Component, OnInit } from '@angular/core';

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from "rxjs/BehaviorSubject";

import 'rxjs/add/observable/bindCallback';
import 'rxjs/add/observable/bindNodeCallback';
import 'rxjs/add/observable/combineLatest';
import 'rxjs/add/observable/concat';
import 'rxjs/add/observable/defer';
import 'rxjs/add/observable/empty';
import 'rxjs/add/observable/forkJoin';
import 'rxjs/add/observable/from';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/fromEventPattern';
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/observable/generate';
import 'rxjs/add/observable/if';
import 'rxjs/add/observable/interval';
import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/race';
import 'rxjs/add/observable/never';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/onErrorResumeNext';
import 'rxjs/add/observable/pairs';
import 'rxjs/add/observable/range';
import 'rxjs/add/observable/using';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/timer';
import 'rxjs/add/observable/zip';
import 'rxjs/add/observable/dom/ajax';
import 'rxjs/add/observable/dom/webSocket';
import 'rxjs/add/operator/buffer';
import 'rxjs/add/operator/bufferCount';
import 'rxjs/add/operator/bufferTime';
import 'rxjs/add/operator/bufferToggle';
import 'rxjs/add/operator/bufferWhen';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/combineAll';
import 'rxjs/add/operator/combineLatest';
import 'rxjs/add/operator/concat';
import 'rxjs/add/operator/concatAll';
import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/concatMapTo';
import 'rxjs/add/operator/count';
import 'rxjs/add/operator/dematerialize';
import 'rxjs/add/operator/debounce';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/defaultIfEmpty';
import 'rxjs/add/operator/delay';
import 'rxjs/add/operator/delayWhen';
import 'rxjs/add/operator/distinct';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/distinctUntilKeyChanged';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/exhaust';
import 'rxjs/add/operator/exhaustMap';
import 'rxjs/add/operator/expand';
import 'rxjs/add/operator/elementAt';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/finally';
import 'rxjs/add/operator/find';
import 'rxjs/add/operator/findIndex';
import 'rxjs/add/operator/first';
import 'rxjs/add/operator/groupBy';
import 'rxjs/add/operator/ignoreElements';
import 'rxjs/add/operator/isEmpty';
import 'rxjs/add/operator/audit';
import 'rxjs/add/operator/auditTime';
import 'rxjs/add/operator/last';
import 'rxjs/add/operator/let';
import 'rxjs/add/operator/every';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mapTo';
import 'rxjs/add/operator/materialize';
import 'rxjs/add/operator/max';
import 'rxjs/add/operator/merge';
import 'rxjs/add/operator/mergeAll';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/mergeMapTo';
import 'rxjs/add/operator/mergeScan';
import 'rxjs/add/operator/min';
import 'rxjs/add/operator/multicast';
import 'rxjs/add/operator/observeOn';
import 'rxjs/add/operator/onErrorResumeNext';
import 'rxjs/add/operator/pairwise';
import 'rxjs/add/operator/partition';
import 'rxjs/add/operator/pluck';
import 'rxjs/add/operator/publish';
import 'rxjs/add/operator/publishBehavior';
import 'rxjs/add/operator/publishReplay';
import 'rxjs/add/operator/publishLast';
import 'rxjs/add/operator/race';
import 'rxjs/add/operator/reduce';
import 'rxjs/add/operator/repeat';
import 'rxjs/add/operator/repeatWhen';
import 'rxjs/add/operator/retry';
import 'rxjs/add/operator/retryWhen';
import 'rxjs/add/operator/sample';
import 'rxjs/add/operator/sampleTime';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/sequenceEqual';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/single';
import 'rxjs/add/operator/skip';
import 'rxjs/add/operator/skipUntil';
import 'rxjs/add/operator/skipWhile';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/subscribeOn';
import 'rxjs/add/operator/switch';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/switchMapTo';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/takeLast';
import 'rxjs/add/operator/takeUntil';
import 'rxjs/add/operator/takeWhile';
import 'rxjs/add/operator/throttle';
import 'rxjs/add/operator/throttleTime';
import 'rxjs/add/operator/timeInterval';
import 'rxjs/add/operator/timeout';
import 'rxjs/add/operator/timeoutWith';
import 'rxjs/add/operator/timestamp';
import 'rxjs/add/operator/toArray';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/window';
import 'rxjs/add/operator/windowCount';
import 'rxjs/add/operator/windowTime';
import 'rxjs/add/operator/windowToggle';
import 'rxjs/add/operator/windowWhen';
import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/operator/zip';
import 'rxjs/add/operator/zipAll';
@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
    title = 'app works!';
    obs = Observable.of(1, 2, 3, 4);

    ngOnInit() {
        this.howToHandleErrorV1();
        this.howToHandleErrorV2();
        this.howToUseRetry();
        this.mergeObservableAndThrowError();
        this.mergeObservableAndErrorResumeNext();
        this.mergeObservableAndErrorCatch();    
    }

    
    
    /*
     * This uses Catch for V1. This introduces Closure. It is effectively the same as V2.
     * */
    howToHandleErrorV1() {
        this.obs
            .map(x => {
                if ( x === 3 ) {
                    throw 'I hate threes'; // When it hitted error it actually unsubscribe itself at x === 3 of throw error
                }
                return x;
            })
            .catch(err => Observable.throw('Caught error here Observable.throw')) // continue go down the error path use Observable.throw
            .catch(err => Observable.of('Caught error here Observable.of')) // catch just use Observable.of
            .subscribe(
                x => console.log(x),
                err => console.error(err), // If not catch any where, the I hate threes errors will be propagated to here
                () => console.log('done completed')
            );
    }

    
    
    /*
     * There is a difference between V1 and V2. For V2 it is using onErrorResumeNext which
     * */
    howToHandleErrorV2() {
        let good = Observable.of('Caught error here Observable.of');

        this.obs
            .map(x => {
                if ( x === 3 ) {
                    throw 'I hate threes'; // When it hit error it actually unsubscribe itself at x === 3 of throw error
                }
                return x;
            })
            .onErrorResumeNext(good) // To catch just use Observable.of
            .subscribe(
                x => console.log(x),
                err => console.error(err), // If not catch any where, the I hate threes errors will be propagated to here
                () => console.log('done completed')
            );
    }

    
    
    /*
     * For this we use see it retries three times then console.error(err);
     * So retryWhen is for trying network connection websocket
     * */
    howToUseRetry() {
        this.obs
            .map(x => {
                if ( x === 3 ) {
                    throw 'I hate threes'; // When it hitted error it actually unsubscribe itself at x === 3 of throw error
                }
                return x;
            })
            .retry(3) // retry three times
            .retryWhen(err => err.delay(2000).take(3)) // similar but with 2 seconds delay and the error is not propagated.
            .retryWhen(err => err.delay(2000).take(3).concat(Observable.throw('bad'))) // this it would throw an error.
            .subscribe(
                x => console.log(x),
                err => console.error(err), // If not catch any where, the I hate threes errors will be propagated to here
                () => console.log('done completed')
            );
    }
/*
 * Using observable merge operator
 * */
mergeObservableAndThrowError() {
    let mergedObs = Observable.merge(
        this.obs, //1, 2, 3, 4
        Observable.throw('Stop Error'),
        Observable.from(this.array), //0, 1, 2, 3, 4, 5
        Observable.of(999) //999,
    );

    mergedObs.subscribe(
        val => console.log(val), //this should show 1, 2, 3, 4, Stop Error
        error => console.log(error),
        () => console.log("completed")
    );
}

/* Using observable onErrorResumeNext just like merge operator
 * */
mergeObservableAndErrorResumeNext() {
    let mergedObs = Observable.onErrorResumeNext(
        this.obs, //1, 2, 3, 4
        Observable.throw('Stop Error'),
        Observable.from(this.array), //0, 1, 2, 3, 4, 5
        Observable.of(999) //999,
    );

    mergedObs.subscribe(
        val => console.log(val), //this should show 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 999
        error => console.log(error),
        () => console.log("completed")
    );
}
/*
 * Using observable merge operator and catch
 * */
mergeObservableAndErrorCatch() {
    let mergedObs = Observable.merge(
        this.obs, //1, 2, 3, 4
        Observable.throw('Stop Error'),
        Observable.from(this.array), //0, 1, 2, 3, 4, 5
        Observable.of(999) //999,
    ).catch(e => {
        console.log(e);
        return Observable.of('catch error here');
    });

    mergedObs.subscribe(
        val => console.log(val), //this should show 1, 2, 3, 4, Stop Error, Catch Error Here
        error => console.log(error),
        () => console.log("completed")
    );
}
}

map vs flatMap in RxJS

Transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable. See my GitHub repo.

obs = Observable.of(1, 2, 3, 4);

ngOnInit() {
    this.usingMap();
    this.usingMapToMakeInnerObservable();
    this.usingMapAndMergeAll();
    this.usingFlatMap();
}usingMap() {
    this.obs
        .map(x => x * 2) // transform the input by multiple of 2
        .subscribe(
            x => console.log(x),
            err => console.error(err),
            () => console.log('done completed')
        );
}
usingMapToMakeInnerObservable() {
    this.obs
        .map(x => Observable.timer(500).map(() => x + 3)) // transform the input wrapping it with another observable and addition of 3
        //.map(x => Observable.timer(500).map((x) => x + 3)) // !!! REMEMBER Not the same as the immediate above
        .subscribe(
            x => console.log(x),
            err => console.error(err),
            () => console.log('done completed')
        );
}
// Map and Merge all is the same as just one FlatMap
usingMapAndMergeAll() {
    this.obs
        .map(x => Observable.timer(500).map(() => x + 3)) // transform the input wrapping it with another observable and addition of 3
        .mergeAll()
        .subscribe(
            x => console.log(x),
            err => console.error(err),
            () => console.log('done completed')
        );
}
// Flat map is the same as map then merge all
// transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable
usingFlatMap() {
    this.obs
        .flatMap(x => Observable.timer(500).map(() => x + 10)) // transform the input wrapping it with another observable and addition of 10
        .subscribe(
            x => console.log(x),
            err => console.error(err),
            () => console.log('done completed')
        );
}

Transforming pure Javascript array vs. Observable from array

See GitHub for source code

array = [0, 1, 2, 3, 4, 5];

ngOnInit() {
    this.setArrayToObservableThenTransform();
}/*
* This keeps creating new array. It is good that it creates new array of arr for immutability.
* But it's bad because there is clean up and resource intensive for mobile
* */
transformArray() {
    let result = this.array
        .filter(( x, i, arr ) => {
            console.log('filtering ' + x);
            console.log('is the source array ' + (arr === this.array));
            return x
        })
        .map(( x, i, arr ) => {
            console.log('mapping ' + x);
            console.log('is the source array ' + (arr === this.array));
            return x + '!';
        })
        .reduce(( r, x, i, arr ) => {
            console.log('reducing ' + x);
            return r + x;
        }, '--');

    console.log(result);
}

/*
* This is more efficient for resource management because it linearly scans and discard when not right
* */
setArrayToObservableThenTransform() {    let obsArray = Observable.from(this.array); // Use Observable.from() instead of Observable.of(). There is diff.    obsArray
        .filter(( x: any ) => {
            console.log('filtering ' + x);
            return x
        })
        .map(( x ) => {
            console.log('mapping ' + x);
            return x + '!';
        })
        .reduce(( r, x ) => {
            console.log('reducing ' + x);
            return r + x;
        }, '--')
        .subscribe(
            x => console.log(x)
        );
}

Using reduce and scan to aggregate RxJs data

See GitHub for source code

array = [0, 1, 2, 3, 4, 5];ngOnInit() {
    this.reduceArray();
    this.reduceObservableArray();
    this.reduceObservableArray_Abstract2();
    this.scanObservableArray();
}
/*
* This is the same as reduceObservableArray()
* */
reduceArray() {
    let result = this.array.reduce(
        (accumulator, currentValue) => accumulator + currentValue, 3
    ); // 3 is the init value.
    console.log('reduceArray ' + result); // output 18 => 3 + (0 ... 5)
}
/*
* This is the same as reduceArray()
* But this waits for all the arrays to finish emitting before reducing them to one single number
* See the next method to understand better
* */
reduceObservableArray() {
    let obsArray = Observable.from(this.array);
    obsArray.reduce(
        (accumulator, currentValue) => accumulator + currentValue, 3
    ).subscribe(
        val => console.log('reduceObservableArray ' + val)
    );
}
/*
* The exact same reduce function/method as of reduceObserableArray() above
* This proves that it waits for all 6 numbers to come in then reduce them
* */
reduceObservableArray_Abstract2() {
    let obsArray = Observable.interval(1000).take(6); //emits 6 times of 0, 1, 2, 3, 4, 5
    obsArray.reduce(
        (accumulator, currentValue) => accumulator + currentValue, 3
    ).subscribe(
        val => console.log('reduceObservableArray_Abstract2 ' + val)
    );
}
/*
* This is the same as the above reduceObserableArray_Abstract2()
* except this is using scan instead of reduce
* */
scanObservableArray() {
    let obsArray = Observable.interval(1000).take(6); //emits 6 times of 0, 1, 2, 3, 4, 5
    obsArray.scan(
        (accumulator, currentValue) => accumulator + currentValue, 3
    ).subscribe(
        val => console.log('scanObservableArray() ' + val)
    );
}

Create, next, and subscribe to Subject and BehaviorSubject

There is a Stack Overflow thread which discussed about the difference between Subject and BehaviorSubject. It’s worth understanding.

Also see my personal GitHub for source code.

app.component.ts

import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from "rxjs/BehaviorSubject";
// create subject
// there is no need for initial value
subject = new Subject<boolean>();

// create behaviorSubject which require initial value
// true is an initial value. if there is a subscription
// after this, it would get true value immediately
behaviorSubject = new BehaviorSubject<boolean>(true);
ngOnInit() {
    this.subject.next(false); /* Subject subscription wont get anything at this point before the subscribeSubject() */    this.subscribeSubject();
    this.subscribeBehaviorSubject();
}
/*
* Push the next val into the behavior subject
* */
nextSubject(val: boolean) {
    this.subject.next(val);
}

/*
* Any values push into the subject would not be can shown
* before this subscribeSubject() is called
* */
subscribeSubject() {
    this.subject
        //.take(1) //when we include .take(1) we will have a complete. Without this it will continue subscribing
        .subscribe(
            val => console.log(val),
            err => console.error(err),
            () => console.log('completed')
        );
}

/*
* This is the proper way to return a subject as observable
* */
getSubject(): Observable<boolean> {
    return this.subject.asObservable();
}

/*
 * Push the next val into the behavior subject
 * */
nextBehaviorSubject(val: boolean) {
    this.behaviorSubject.next(val);
}

/*
* For angular Behavior subject for a data service as a angular service often initializes
* before component and behavior subject ensures that the component consuming the
* service receives the last updated data even if there are no new
* updates since the component's subscription to this data.
* */
subscribeBehaviorSubject() {
    this.behaviorSubject
        // .first()
        .subscribe(
            val => console.log(val),
            err => console.error(err),
            () => console.log('completed')
        );
}

app.component.html

Subject:
<button (click)="nextSubject(true)">true</button>
<button (click)="nextSubject(false)">false</button>

<br>
BehaviorSubject:
<button (click)="nextBehaviorSubject(true)">true</button>
<button (click)="nextBehaviorSubject(false)">false</button>

Finally operator

usingFinallyOperator() {
    Observable
        .interval(500)
        .take(4)
        .finally(() => console.log('End of the observable, Hello World'))
        .subscribe(
            val => console.log('count taker ' + val)
        );
}

Stopping / Intercepting Observable

Imagine using Gmail where it allows you to undo email sent? We can produce similar experience with Observable

// subscription is created when an observable is being subscribed
subscription: Subscription;

// boolean variable for showing stop observable using takeWhile operator
isTrue: boolean = true;
/*
* basic interval can be used as delay too
* Imagine Gmail allows you to send and undo send within 4 seconds of sending
* Use Case: Perform an action 8 seconds later then intercept if user choose to undo the action
* */basicInterval() {
    let undoInSeconds: number = 8;
    this.subscription = Observable
            .interval(1000)
            .take(undoInSeconds)
            .takeWhile(() => this.isTrue)
            .subscribe(
                (val: number) => {
                    console.log(`${val + 1} seconds...         UNDO`);
                    ( val === (undoInSeconds - 1) ) ? console.log('Email sent / Action performed') : null;
                }
            );
}
/*
* This is to stop observable from continuing performance
* Use Case: Stop observable from running like how Gmail could undo email being sent
* */stopObservableUsingUnsubscribe() {
    if (!!this.subscription) {
        this.subscription.unsubscribe();
        console.log('subscription: Subscription is unsubscribed');
    }
}

/*
* This is also to stop observable from continuing performance
* This method is more preferable than subscribing method then unsubscribe
 * Use Case: Stop observable from running like how Gmail could undo email being sent
* */stopObservableUsingTakeWhile() {
    this.isTrue = false;
}

Perform conditional Reactive Form validation

This is my approach to performing conditional validation when using Angular. We will minimally manipulate the Observable of RxJS in this example. Let’s try by creating or using app.component.ts.

1 . Create a form that has two form controls reason and otherReason.

/* * Refer to angular official guide at https://angular.io/guide/reactive-forms on how to create reactive form with form controls * */createForm() {
    this.form = this.formBuilder.group({      reason: ['', Validators.required ],
      otherReason: [''],    });
}

2 . Create two methods addValidator and removeValidator.

/*  * For conditional form validation use  * */
private addValidator( control: AbstractControl, newValidator ){
    let existingValidators = control.validator;
    control.setValidators(Validators.compose([ existingValidators, newValidator ]));
    control.updateValueAndValidity();
}

/*  * For conditional form validation use  * */
private removeValidator( control: AbstractControl ){
    control.clearValidators();
    control.updateValueAndValidity();
}

3 . Create third method called conditionalFormValidation.

conditionalFormValidation( parentField, childField, matchValue = 'Others' )
{
    this.form
        .get(parentField)
        .valueChanges
        .forEach(( value: string ) => {
            const childFieldControl: AbstractControl = this.form.get(childField);
            if ( value === matchValue ) {
                this.addValidator(childFieldControl, Validators.required);
            } else {
                this.removeValidator(childFieldControl);
            }
        });
}

4 . Create or add to ngOnInit method

ngOnInit() {    
    this.createForm();
    this.conditionalFormValidation('reason', 'otherReason');
}

The outcome should illustrate that if we select ‘Others’ option in the dropdown list of reason, it should make otherReason form control field as required.

Drop-down selection for reason e.g. reason for absent from work
On selecting Others, the conditional form validation would make otherReason field turn into a required field

Perform manual operations after reading from Firebase database

When using AngularFire2 with Angular + Firebase, in getting a list of data from Firebase, we will get one observable instance but within that one observable is an array of N size. We can manually filter the array inside that one observable instance using arr.filter. It is different from RxJS .filter operator. Of course we can also flatten what is inside an array using .flatMap() operator. However, we’re going use JavaScript array filtering, instead of non-observable filtering, right after getting an observable object.

this.db.list(`/review`)
       .map(arr => arr.filter(item => item.rating > 3));

We can also reverse an array using JavaScript array reverse function. See reference. On a side note, using negative timestamp to reverse Firebase display is also another option.

this.db.list('/review')
       .map(arr => { return arr.reverse(); });

Besides those above, we can also use the response returned from AngularFire2 to perform “local filter/search”. This result can be valuable for autocomplete filtered list or searches. However, this approach below suffers severely in performance issue where at the magnitude of the size of the returned response from AngularFire2. E.g. if the list has N items. It has to iterate at least 1N. Perhaps an average of 2N.

getFilteredClientList(searchQuery: string): Observable<Client[]> {
    let query = searchQuery.trim().toLowerCase();

    return this.db.list(`/client`)
      .map((arr: Client[]) => {
        return arr.filter((item: Client) => {
          return item.email.toLowerCase().indexOf(query) === 0
            || item.given_name.toLowerCase().indexOf(query) === 0
            || item.family_name.toLowerCase().indexOf(query) === 0
        });
      });
  }

Angular | Working with Projects

TL;DR

Live Demo or Source Code

Create base application

Main ideas comes from here: Combining Multiple Angular Applications into a Single One

Generate base app and workspace

In the following steps, we will create a main application and 2 sub-applications.

Each Sub-Application consists of 2 Views (Pages) and a common navigation bar.

As a result, we will get the following directory structure:

$ ng new workspace --routing --style scss
$ cd workspace

Create additional apps

$ ng generate application --routing app1 --style scss
$ ng generate application --routing app2 --style scss

Generate a component

For each application, we create the 3 components: View 1, View 2 and a NavBar.

  • The parameter --project defines the application, where we will add the component.
  • The Parameter --module defines the application module class, where we add the definition the the newly creates components, in this case app#/src/app/app.modules.ts
ng generate component components/view1 --project=app1 --module=app.module.ts
ng generate component components/view2 --project=app1 --module=app.module.ts
ng generate component components/nav   --project=app1 --module=app.module.ts

ng generate component components/view1 --project=app2 --module=app.module.ts
ng generate component components/view2 --project=app2 --module=app.module.ts
ng generate component components/nav   --project=app2 --module=app.module.ts

ng generate component components/nav

Customize Components: Main App

Edit src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { App1SharedModule } from 'projects/app1/src/app/app.shared.module';
import { App2SharedModule } from 'projects/app2/src/app/app.shared.module';
import { NavComponent } from './components/nav/nav.component';

@NgModule({
    declarations: [AppComponent, NavComponent],
    imports: [
        BrowserModule,
        AppRoutingModule,
        App1SharedModule.forRoot(),
        App2SharedModule.forRoot()
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule {}

Edit src/app/app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { App1SharedModule } from 'projects/app1/src/app/app.shared.module';
import { App2SharedModule } from 'projects/app2/src/app/app.shared.module';

const routes: Routes = [
    { path: 'app1', component: App1SharedModule },
    { path: 'app2', component: App2SharedModule },
    { path: '**', redirectTo: 'app1/view1' }
];

@NgModule({
    imports: [
        RouterModule.forRoot(routes),
        App1SharedModule.forRoot(),
        App2SharedModule.forRoot()
    ],
    exports: [RouterModule]
})
export class AppRoutingModule {}

Edit src/app/app.component.ts

export class AppComponent {
    title = 'base-app';
}

Replace src/app/app.component.html

The main app page consists of two areas:

  • the header part with the welcome text, an image and the navigation bar to the 2 apps
  • the application part, depending of the selected application
<div style="text-align:center">
    <h1>Welcome to {{ title | uppercase }}</h1>
    <img width="300" alt="Angular Logo"
        src="https://upload.wikimedia.org/wikipedia/commons/c/cf/Angular_full_color_logo.svg">
</div>

<app-nav></app-nav>
<hr />
<router-outlet></router-outlet>

Replace src/app/components/nav/nav.component.html

<div style="text-align:center">
    <a routerLink="/app1">App 1</a> | <a routerLink="/app2">App 2</a>
</div>

Customize Components: App1

Edit projects/app1/src/app/app.module.ts

import { View1Component } from './components/view1/view1.component';
import { View2Component } from './components/view2/view2.component';
import { NavComponent }   from './components/nav/nav.component';

@NgModule({
    declarations: [AppComponent, View1Component, View2Component, NavComponent],
    ...

Edit projects/app1/src/app/app-routing.module.ts

import { View1Component } from './components/view1/view1.component';
import { View2Component } from './components/view2/view2.component';

const routes: Routes = [
        { path: 'app1/view1', component: View1Component },
        { path: 'app1/view2', component: View2Component },
        { path: 'app1', redirectTo: 'app1/view1' }
    ];

Create projects/app1/src/app/app.shared.module.ts

import { NgModule, ModuleWithProviders } from '@angular/core';
import { AppModule } from './app.module';

const providers = [];

@NgModule({})
export class App1SharedModule {
    static forRoot(): ModuleWithProviders {
        return {
            ngModule: AppModule,
            providers
        };
    }
}

Replace projects/app1/src/app/app.component.html

<router-outlet></router-outlet>

Replace projects/app1/src/app/components/nav/nav.component.html

<a routerLink="/app1/view1">View 1</a> | <a routerLink="/app1/view2">View 2</a>

Add to projects/app1/src/app/components/view2/view2.component.ts

export class View1Component implements OnInit {
    title = 'App1: View 1';
    ...

Replace projects/app1/src/app/components/view1/view1.component.html

<app-nav></app-nav>
<p>{{ title }}</p>

Replace projects/app1/src/app/components/view2/view2.component.html

<app-nav></app-nav>
<p>{{ title }}</p>

Customize Components: App2

Do the same as for App1, but replace every App1 with App2and every app1 with app2

Angular | Starting with Capacitor

Introduction

Install Angular

$ npm -g install @angular/cli

Create sample app

❯ ng new app-capacitor
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS   [ https://sass-lang.com/documentation/syntax#scss]

❯ cd app-capacitor

Change output path to capacitor defaults

Change line in angular.json

      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist",

 Add capacitor support

❯ ng add @capacitor/angular
ℹ Using package manager: npm
✖ Unable to find compatible package.  Using 'latest'.
✖ Package has unmet peer dependencies. Adding the package may not succeed.

The package @capacitor/angular will be installed and executed.
Would you like to proceed? Yes

Capacitor initialisieren

❯ npx cap init
[?] What is the name of your app?
    This should be a human-friendly app name, like what you'd see in the App Store.
√ Name ... app-capacitor
[?] What should be the Package ID for your app?
    Package IDs (aka Bundle ID in iOS and Application ID in Android) are unique identifiers for apps. They must be in
    reverse domain name notation, generally representing a domain name that you or your company owns.
√ Package ID ... com.example.app
√ Creating capacitor.config.ts in D:\CLOUD\Online-Seminare\Kurse\Angular\Einsteiger\App_Capacitor in 5.31ms
[success] capacitor.config.ts created!

Next steps:
https://capacitorjs.com/docs/v3/getting-started#where-to-go-next
[?] Join the Ionic Community! 💙
    Connect with millions of developers on the Ionic Forum and get access to live events, news updates, and more.
√ Create free Ionic account? ... no
[?] Would you like to help improve Capacitor by sharing anonymous usage data? 💖
    Read more about what is being collected and why here: https://capacitorjs.com/telemetry. You can change your mind at
    any time by using the npx cap telemetry command.
√ Share anonymous usage data? ... no

Build your app

We will need the dist directory with the web files

❯ ng build --prod
Option "--prod" is deprecated: No need to use this option as this builder defaults to configuration "production".
✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.

Initial Chunk Files               | Names         |      Size
main.b633c9096acdb457c421.js      | main          | 212.34 kB
polyfills.e4574352eda6eb439793.js | polyfills     |  35.95 kB
runtime.d66bb6fe709ae891f100.js   | runtime       |   1.01 kB
styles.31d6cfe0d16ae931b73c.css   | styles        |   0 bytes

                                  | Initial Total | 249.29 kB

Build at: 2021-05-28T09:32:28.905Z - Hash: ed2ed2d661b0d58b48f2 - Time: 28225ms
❯ npm install @capacitor/ios @capacitor/android
npm WARN @capacitor/angular@1.0.3 requires a peer of rxjs@~6.4.0 but none is installed. You must install peer dependencies yourself.
npm WARN @capacitor/angular@1.0.3 requires a peer of typescript@~3.4.3 but none is installed. You must install peer dependencies yourself.
npm WARN ajv-keywords@3.5.2 requires a peer of ajv@^6.9.1 but none is installed. You must install peer dependencies yourself.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.2 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules\webpack-dev-server\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

+ @capacitor/ios@3.0.0
+ @capacitor/android@3.0.0
added 2 packages from 1 contributor, removed 1 package and audited 1375 packages in 7.385s

88 packages are looking for funding
  run `npm fund` for details

found 35 moderate severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details

Add Platform Android

❯ npx cap add android
√ Adding native android project in android in 167.07ms
√ Syncing Gradle in 1.45ms
√ add in 169.83ms
[success] android platform added!
Follow the Developer Workflow guide to get building:
https://capacitorjs.com/docs/v3/basics/workflow
√ Copying web assets from dist to android\app\src\main\assets\public in 55.26ms
√ Creating capacitor.config.json in android\app\src\main\assets in 3.50ms
√ copy android in 164.93ms
√ Updating Android plugins in 7.76ms
√ update android in 75.79ms

Open Android Studio to build App

❯ npx cap open android
[info] Opening Android project at: android.