Designing Angular Presentation Components with Loosely Coupled Services | Task

Ole Ersoy
Mar - 08  -  2 min

Scenario

We want to create a Angular Material social login button in a shared library that when clicked will call the method signInWithGoogle() on a service that that has been assigned to the auth property of the button. The service should implement this interface:

export interface IGoogleSocialAuth {
   signOut()
   signInWithGoogle()
}

Analysis

We are illustrating that it is possible to do this, but it should probably be considered an anti pattern.

The reason is that it couples the IGoogleSocialAuth interface to the component.

Using an EventEmitter to signal that the button is clicked, as done in Designing an Angular Social Login Button Presentation Component is usually a better approach.

Approach

The button design looks like this. It is disabled by default, and can be enabled by assign an Observable<boolean> to the property isTOSAccepted$ that emits true when the button should be enabled.

import { Component, OnInit } from '@angular/core';
import { Input } from '@angular/core';
import { Observable, of } from 'rxjs';
export interface IGoogleSocialAuth {
    signOut()
    signInWithGoogle()
}
@Component({
    selector: 'fs-login-button',
    templateUrl: './login-button.component.html',
    styleUrls: ['./login-button.component.scss']
})
export class LoginButtonComponent implements OnInit {
    @Input()
    public auth: IGoogleSocialAuth
    @Input()
    public isTOSAccepted$: Observable<boolean> = of(false)
    ngOnInit(): void { }
}

The template:

<button [disabled]="!(isTOSAccepted$ | async)"
(click)="auth.signInWithGoogle()"
mat-button
style="padding: 1rem;">
<mat-icon
[svgIcon]="SVG_CONSTANTS.GOOGLE_LOGO.name"></mat-icon> &nbsp; &nbsp; Login With Google
</button>

Test

Within app.component.ts declare the following class:

class Auth {
   signOut() {
       console.log("Signing Out")
    }
    signInWithGoogle() {
        console.log("Signing in with Google")
   }
}

Create an instance of it and assign it to the auth property on app.component.ts.

We will create an Observable<boolean> instance that emits true as well:

auth = new Auth()
isTOCAccepted$: Observable<boolean> = of(true)

Within the app.component.html template declare the button:

<fs-login-button [auth]="auth" [isTOSAccepted$]="isTOCAccepted$"></fs-login-button>

When the button is clicked it will log Signing in with Google.