How to deal with a modal shown in a guard

I have a guard implemented like this:

I have a guard implemented like this:

@Injectable()
export class CustomerGuard implements CanActivate {

constructor(
private authService: AuthenticationService,
private dialog: MatDialog
) { }

canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {

if (this.authService.isCustomer) {
  return true;
}
const dialog = this.dialog.open(SigninModalComponent, {
  data: {
    errorMessage: this.authService.isLoggedIn ?
      'You don\t have access to this page, please use an appropriate account' :
      'Please authenticate to access this page'
  }
});

return dialog.afterClosed().pipe(
  map(() =&gt; {
    return this.authService.isCustomer;
  })
);

}
}

When I type an unauthorized route in my browser's address bar, the server-side rendering shows an inert modal, then when the client-side takes over, another working modal is shown where I can successfully authenticate and access the requested route.

The problem is that the server-side rendered modal never disappears...

Is there a clean solution to this problem that wouldn't imply not to show the modal on server side?

TypeScript, Angular, Firebase & Angular Material Master class Tutorial

As the course progresses, you'll get familiar with: TypeScript, Angular Application Architecture, and Angular CLI. Angular Modules and Angular Components. Angular's Component LifeCycle Hooks....

As the course progresses, you'll get familiar with: TypeScript, Angular Application Architecture, and Angular CLI. Angular Modules and Angular Components. Angular's Component LifeCycle Hooks....

Learn More

Angular 7 (formerly Angular 2) - The Complete Guide

Learn and Understand AngularJS

Angular Crash Course for Busy Developers

The Complete Angular Course: Beginner to Advanced

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Become a JavaScript developer - Learn (React, Node,Angular)

Angular (Full App) with Angular Material, Angularfire & NgRx

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript

Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript

Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript. This tutorial gets you off the ground with Angular. We are going to use the official CLI (command line) tool to generate boilerplate code.

Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript. This tutorial gets you off the ground with Angular. We are going to use the official CLI (command line) tool to generate boilerplate code.

1. Prerequisites

This tutorial is targeted to people familiar with JavaScript and HTML/CSS. You also will need:

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

You can verify by typing:

node --version
# v10.8.0
npm --version
# 6.2.0

If you get the versions Node 4.x.x and NPM 3.x.x. or higher you are all set. If not you have to get the latest versions.

Let’s move on to Angular. We are going to create a Todo app.

2. Understanding ng new

Angular CLI is the best way to get us started. We can download the tool and create a new project by running:

# install angular-cli globally
npm install -g @angular/[email protected]
# npm install -g @angular/cli # get latest

# Check angular CLI is installed
ng --version
# Angular CLI: 6.1.2

If the versions don’t match then you can remove previously installed angular CLI with the following commands:

npm uninstall -g @angular/cli
yarn global remove @angular/cli

Once you have the right version, do:

# create a new project
ng new Todos --style=scss

Note The last command takes some minutes. Leave it running and continue reading this tutorial.

The command ng new will do a bunch of things for us:

  1. Initialize a git repository
  2. Creates an package.json files with all the Angular dependencies.
  3. Setup TypeScript, Webpack, Tests (Jasmine, Protractor, Karma). Don’t worry if you don’t know what they are. We are going to cover them later.
  4. It creates the src folder with the bootstrapping code to load our app into the browser
  5. Finally, it does an npm install to get all the packages into node_modules.

Let’s run the app!

# builds the app and run it on port 9000
ng serve ---port 9000

Open your browser on http://localhost:9000/, and you should see “Loading…” and then it should switch to “Welcome to app!”. Awesome!

Now let’s dive into the src folder and get familiarized with the structure.

2.1 package.json

Open the package.json file and take a look at the dependencies. We have all the angular dependencies with the prefix @angular/.... Other dependencies are needed for Angular to run, such as RxJS, Zone.js, and some others. We are going to cover them in other posts.

2.2 src/index.html

We are building an SPA (single page application), so everything is going to be loaded into the index.html. Let’s take a look in the src/index.html. It’s pretty standard HTML5 code, except for two elements that are specific for our app:

  1. Initialize a git repository
  2. Creates an package.json files with all the Angular dependencies.
  3. Setup TypeScript, Webpack, Tests (Jasmine, Protractor, Karma). Don’t worry if you don’t know what they are. We are going to cover them later.
  4. It creates the src folder with the bootstrapping code to load our app into the browser
  5. Finally, it does an npm install to get all the packages into node_modules.

base href is needed for Angular routing to work correctly. We are going to cover Routing later.

<app-root> this is not a standard HTML tag. Our Angular App defines it. It’s an Angular component. More on this later.

2.3 src/main.ts

main.ts is where our application starts bootstrapping (loading). Angular can be used not just in browsers, but also on other platforms such as mobile apps or even desktop apps. So, when we start our application, we have to specify what platform we want to target. That’s why we import: platform-browser-dynamic. Notice that we are also importing the AppModule from ./app.

The most important line is:

platformBrowserDynamic().bootstrapModule(AppModule);

We are loading our AppModule into the browser platform. Now, let’s take a look at the ./app/app.module.tsdirectory.

2.4 App directory

The app directory contains the components used to mount the rest of the application. In there the <app-root> that we so in the index.html is defined. Let’s start with app.module

app.module.ts

We are going to be using this file often. The most important part is the metadata inside the @NgModule. There we have declarationsimportsproviders and bootstrap.

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

app.component.ts

AppComponent looks a little similar to the app module, but instead of @NgModule we have @Component. Again, the most important part is the value of the attributes (metadata). We have selectortemplateUrl and styleUrls:

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

Inside the AppComponent class you can define variables (e.g. title) that are used in the templates (e.g. Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript).

Let’s change the title from Welcome to Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript!to Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript. Also, remove everything else.
Test your changes running:

ng serve ---port 9000

You should see the new message.

[changes diff]

3. Creating a new Component with Angular CLI

Let’s create a new component to display the tasks. We can quickly create by typing:

ng generate component todo

This command will create a new folder with four files:

create src/app/todo/todo.component.css
create src/app/todo/todo.component.html
create src/app/todo/todo.component.spec.ts
create src/app/todo/todo.component.ts

And it will add the new Todo component to the AppModule:

UPDATE src/app/app.module.ts

Go ahead and inspect each one. It will look similar to the app components. Let ‘s add our new component to the App component.

[changes diff]

Go to src/app/app.component.html, and replace everything with:

src/app/app.component.html

<app-todo></app-todo>

If you have ng serve running, it should automatically update and show todo works!

[changes diff]

4. Todo Template

“todo works!” is not useful. Let’s change that by adding some HTML code to represent our todo tasks. Go to the src/app/todo/todo.component.html file and copy-paste this HTML code:

<section class="todoapp">

  <header class="header">
    <h1>Todo</h1>
    <input class="new-todo" placeholder="What needs to be done?" autofocus>
  </header>

  <!-- This section should be hidden by default and shown when there are todos -->
  <section class="main">

    <ul class="todo-list">
      <!-- These are here just to show the structure of the list items -->
      <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
      <li class="completed">
        <div class="view">
          <input class="toggle" type="checkbox" checked>
          <label>Install angular-cli</label>
          <button class="destroy"></button>
        </div>
        <input class="edit" value="Create a TodoMVC template">
      </li>
      <li>
        <div class="view">
          <input class="toggle" type="checkbox">
          <label>Understand Angular2 apps</label>
          <button class="destroy"></button>
        </div>
        <input class="edit" value="Rule the web">
      </li>
    </ul>
  </section>

  <!-- This footer should hidden by default and shown when there are todos -->
  <footer class="footer">
    <!-- This should be `0 items left` by default -->
    <span class="todo-count"><strong>0</strong> item left</span>
    <!-- Remove this if you don't implement routing -->
    <ul class="filters">
      <li>
        <a class="selected" href="#/">All</a>
      </li>
      <li>
        <a href="#/active">Active</a>
      </li>
      <li>
        <a href="#/completed">Completed</a>
      </li>
    </ul>
    <!-- Hidden if no completed items are left ↓ -->
    <button class="clear-completed">Clear completed</button>
  </footer>
</section>

The above HTML code has the general structure about how we want to represent our tasks. Right now it has hard-coded todo’s. We are going to slowly turn it into a dynamic app using Angular data bindings.

[changes diff]

Next, let’s add some styling!

5. Styling the todo app

We are going to use a community maintained CSS for Todo apps. We can go ahead and download the CSS:

npm install --save todomvc-app-css

This will install a CSS file that we can use to style our Todo app and make it look nice. In the next section, we are going to explain how to use it with the angular-cli.json.

6. Adding global styles to angular.json

angular.json is a special file that tells the Angular CLI how to build your application. You can define how to name your root folder, tests and much more. What we care right now, is telling the angular CLI to use our new CSS file from the node modules. You can do it by adding the following line into the styles array:

"architect": {
  "build": {
    "options": {
      "styles": [
        "src/styles.scss",
        "node_modules/todomvc-app-css/index.css"
      ],
      "scripts": []

If you stop and start ng serve, then you will notice the changes.

We have the skeleton so far. Now we are going to make it dynamic and allow users to add/remove/update/sort tasks. We are going to do two versions one serverless and another one using a Node.js/Express server. We are going to be using promises all the time, so when we use a real API, the service is the only one that has to change.

[changes diff]

7. Todo Service

Let’s first start by creating a service that contains an initial list of tasks that we want to manage. We are going to use a service to manipulate the data. Let’s create the service with the CLI by typing:

ng g service todo/todo

This will create two files:

create src/app/todo/todo.service.spec.ts
create src/app/todo/todo.service.ts

[changes diff]

8. CRUD Functionality

For enabling the create-read-update-delete functionality, we are going to be modifying three files:

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

Let’s get started!

8.1 READ: Get all tasks

Let’s modify the todo.service to be able to get tasks:

import { Injectable } from '@angular/core';

const TODOS = [
  { title: 'Install Angular CLI', isDone: true },
  { title: 'Style app', isDone: true },
  { title: 'Finish service functionality', isDone: false },
  { title: 'Setup API', isDone: false },
];

@Injectable({
  providedIn: 'root'
})
export class TodoService {

  constructor() { }

  get() {
    return new Promise(resolve => resolve(TODOS));
  }
}

Now we need to change our todo component to use the service that we created.

import { Component, OnInit } from '@angular/core';
import { TodoService } from './todo.service';

@Component({
  selector: 'app-todo',
  templateUrl: './todo.component.html',
  styleUrls: ['./todo.component.scss'],
  providers: [TodoService]
})
export class TodoComponent implements OnInit {
  private todos;
  private activeTasks;

  constructor(private todoService: TodoService) { }

  getTodos(){
    return this.todoService.get().then(todos => {
      this.todos = todos;
      this.activeTasks = this.todos.filter(todo => todo.isDone).length;
    });
  }

  ngOnInit() {
    this.getTodos();
  }
}

The first change is importing our TodoService and adding it to the providers. Then we use the constructor of the component to load the TodoService. While we inject the service, we can hold a private instance of it in the variable todoService. Finally, we use it in the getTodos method. This will make a variable todos available in the template where we can render the tasks.

Let’s change the template so we can render the data from the service. Go to the todo.component.html and change what is inside the <ul class="todo-list"> ... </ul> for this one:

<ul class="todo-list">
  <li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" >
    <div class="view">
      <input class="toggle" type="checkbox" [checked]="todo.isDone">
      <label>{{todo.title}}</label>
      <button class="destroy"></button>
    </div>
    <input class="edit" value="{{todo.title}}">
  </li>
</ul>

Also change the 32 in the template from:

<span class="todo-count"><strong>0</strong> item left</span>

replace it with:

<span class="todo-count"><strong>{{activeTasks}}</strong> item left</span>

When your browser updates you should have something like this:

Now, let’s go over what we just did. We can see that we added new data-binding into the template:

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

[changes diff]

8.2 CREATE: using the input form

Let’s start with the template this time. We have an input element for creating new tasks. Let’s listen to changes in the input form and when we click enter it creates the TODO.

<input class="new-todo"
       placeholder="What needs to be done?"
       [(ngModel)]="newTodo"
       (keyup.enter)="addTodo()"
       autofocus>

Notice that we are using a new variable called newTodo and method called addTodo(). Let’s go to the controller and give it some functionality:

private newTodo;

addTodo(){
  this.todoService.add({ title: this.newTodo, isDone: false }).then(() => {
    return this.getTodos();
  }).then(() => {
    this.newTodo = ''; // clear input form value
  });
}

First, we created a private variable that we are going to use to get values from the input form. Then we created a new todo using the todo service method add. It doesn’t exist yet, so we are going to create it next:

add(data) {
  return new Promise(resolve => {
    TODOS.push(data);
    resolve(data);
  });
}

The above code adds the new element into the todos array and resolves the promise. That’s all. Go ahead a test it out creating a new todo element.

You might get an error saying:

Can't bind to 'ngModel' since it isn't a known property of 'input'

To use the two-way data binding you need to import FormsModule in the app.module.ts. So let’s do that.

import { FormsModule } from '@angular/forms';

// ...

@NgModule({
  imports: [
    // ...
    FormsModule
  ],
  // ...
})

Now it should add new tasks to the list!

[changes diff]

8.3 UPDATE: on double click

Let’s add an event listener to double-click on each todo. That way, we can change the content. Editing is tricky since we need to display an input form. Then when the user clicks enter it should update the value. Finally, it should hide the input and show the label with the updated value. Let’s do that by keeping a temp variable called editing which could be true or false.

<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone, editing: todo.editing}" >
  <div class="view">
    <input class="toggle" type="checkbox" [checked]="todo.isDone">
    <label (dblclick)="todo.editing = true">{{todo.title}}</label>
    <button class="destroy"></button>
  </div>
  <input class="edit"
         #updatedTodo
         [value]="todo.title"
         (blur)="updateTodo(todo, updatedTodo.value)"
         (keyup.escape)="todo.editing = false"
         (keyup.enter)="updateTodo(todo, updatedTodo.value)">
</li>

Notice that we are adding a local variable in the template #updateTodo. Then we use it to get the value like updateTodo.value and pass it to a function. We want to update the variables on blur (when you click somewhere else) or on enter. Let’s add the function that updates the value in the component.

Also, notice that we have a new CSS class applied to the element called editing. This is going to take care through CSS to hide and show the input element when needed.

updateTodo(todo, newValue) {
  todo.title = newValue;
  return this.todoService.put(todo).then(() => {
    todo.editing = false;
    return this.getTodos();
  });
}

We update the new todo’s title, and after the service has processed the update, we set editing to false. Finally, we reload all the tasks again. Let’s add the put action on the service.

put(changed) {
  return new Promise(resolve => {
    const index = TODOS.findIndex(todo => todo === changed);
    TODOS[index].title = changed.title;
    resolve(changed);
  });
}

Now, we can edit tasks! Yay!

[changes diff]

8.4 DELETE: clicking X

This is like the other actions. We add an event listenter on the destroy button:

<button class="destroy" (click)="destroyTodo(todo)"></button>

Then we add the function to the component:

destroyTodo(todo) {
  this.todoService.delete(todo).then(() => {
    return this.getTodos();
  });
}

and finally, we add the method to the service:

delete(selected) {
  return new Promise(resolve => {
    const index = TODOS.findIndex(todo => todo === selected);
    TODOS.splice(index, 1);
    resolve(true);
  });
}

Now test it out in the browser!

[changes diff]

9. Routing and Navigation

It’s time to activate the routing. When we click on the active button, we want to show only the ones that are active. Similarly, we want to filter by completed. Additionally, we want to the filters to change the route /active or /completed URLs.

In AppModule, we need to add the router library and define the routes as follows:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { Routes, RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { TodoComponent } from './todo/todo.component';

const routes: Routes = [
  { path: ':status', component: TodoComponent },
  { path: '**', redirectTo: '/all' }
];

@NgModule({
  declarations: [
    AppComponent,
    TodoComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(routes)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

First, we import the routing library. Then we define the routes that we need. We could have said path: 'active', component: TodoComponent and then repeat the same for completed. But instead, we define a parameter called :status that could take any value (allcompletedactive). Any other value path we are going to redirect it to /all. That’s what the ** means.

Finally, we add it to the imports. So the app module uses it. Since the AppComponent is using routes, now we need to define the <router-outlet>. That’s the place where the routes are going to render the component based on the path (in our case TodoComponent).

Let’s go to app/app.component.html and replace <app-todo></app-todo> for <router-outlet></router-outlet>:

<router-outlet></router-outlet>

Test the app in the browser and verify that now the URL is by default [http://localhost:9000/all](http://localhost:9000/all "http://localhost:9000/all").

[changes diff]

9.1 Using routerLink and ActivatedRoute

routerLink is the replacement of href for our dynamic routes. We have set it up to be /all/complete and /active. Notice that the expression is an array. You can pass each part of the URL as an element of the collection.

<ul class="filters">
  <li>
    <a [routerLink]="['/all']" [class.selected]="path === 'all'">All</a>
  </li>
  <li>
    <a [routerLink]="['/active']" [class.selected]="path === 'active'">Active</a>
  </li>
  <li>
    <a [routerLink]="['/completed']" [class.selected]="path === 'completed'">Completed</a>
  </li>
</ul>

What we are doing is applying the selected class if the path matches the button. Yet, we haven’t populate the the path variable yet. So let’s do that:

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

import { TodoService } from './todo.service';

@Component({
  selector: 'app-todo',
  templateUrl: './todo.component.html',
  styleUrls: ['./todo.component.scss'],
  providers: [TodoService]
})
export class TodoComponent implements OnInit {
  private todos;
  private activeTasks;
  private newTodo;
  private path;

  constructor(private todoService: TodoService, private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.params.subscribe(params => {
      this.path = params['status'];
      this.getTodos();
    });
  }

  /* ... */
}

We added ActivatedRoute as a dependency and in the constructor. ActivatedRoute gives us access to the all the route params such as path. Notice that we are using it in the NgOnInit and set the path accordantly.

Go to the browser and check out that the URL matches the active button. But, it doesn’t filter anything yet. Let’s fix that.

[changes diff]

9.2 Filtering data based on the route

To filter todos by active and completed, we need to pass a parameter to the todoService.get.

ngOnInit() {
  this.route.params.subscribe(params => {
    this.path = params['status'];
    this.getTodos(this.path);
  });
}

getTodos(query = ''){
  return this.todoService.get(query).then(todos => {
    this.todos = todos;
    this.activeTasks = this.todos.filter(todo => todo.isDone).length;
  });
}

We added a new parameter query, which takes the path (active, completed or all). Then, we pass that parameter to the service. Let’s handle that in the service:

get(query = '') {
  return new Promise(resolve => {
    let data;

    if (query === 'completed' || query === 'active'){
      const isCompleted = query === 'completed';
      data = TODOS.filter(todo => todo.isDone === isCompleted);
    } else {
      data = TODOS;
    }

    resolve(data);
  });
}

So we added a filter by isDone when we pass either completed or active. If the query is anything else, we return all the todos tasks. That’s pretty much it, test it out!

[changes diff]

10. Clearing out completed tasks

One last UI functionality, clearing out completed tasks button. Let’s first add the click event on the template:

<button class="clear-completed" (click)="clearCompleted()">Clear completed</button>

We referenced a new function clearCompleted that we haven’t create yet. Let’s create it in the TodoComponent:

clearCompleted() {
  this.todoService.deleteCompleted().then(() => {
    return this.getTodos();
  });
}

In the same way we have to create deleteCompleted in the service:

deleteCompleted() {
  return new Promise(resolve => {
    todos = todos.filter(todo => !todo.isDone);
    resolve(todos);
  });
}

We use the filter to get the active tasks and replace the todos array with it.

That’s it we have completed all the functionality.

[changes diff]

11. Deploying the app

You can generate all your assets for production running this command:

ng build --prod

It will minify and concatenate the assets for serving the app faster.

If you want to deploy to a Github page you can do the following:

ng build --prod --output-path docs --base-href "/angular-todo-app/"

Replace /angular-todo-app/ with the name of your project name. Finally, go to settings and set up serving Github pages using the /docs folder:

12. Troubleshooting

If when you compile for production you get an error like:

The variable used in the template needs to be declared as "public". Template is treated as a separate Typescript class.

ERROR in src/app/todo/todo.component.html(7,8): : Property 'newTodo' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(19,11): : Property 'todos' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(38,38): : Property 'activeTasks' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(41,36): : Property 'path' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(44,39): : Property 'path' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(47,42): : Property 'path' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(7,8): : Property 'newTodo' is private and only accessible within class 'TodoComponent'.

Then you need to change private to public like this. This is because the Template in Angular is treated like a separate class.

That’s all folks!

==================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Learn More

☞ Angular 8 (formerly Angular 2) - The Complete Guide

☞ Learn and Understand AngularJS

☞ The Complete Angular Course: Beginner to Advanced

☞ Angular Crash Course for Busy Developers

☞ Angular Essentials (Angular 2+ with TypeScript)

☞ Angular (Full App) with Angular Material, Angularfire & NgRx

☞ Angular & NodeJS - The MEAN Stack Guide

Angular Components with Typescript Mixins

Angular Components with Typescript Mixins

Angular Components with Typescript Mixins .Learn how to maximize reusability in your Angular components by using composition instead of inheritance

This is a follow-up from my previous article about Component Composition with Angular where I listed 3 ways of composing Angular components:

  • Class Inheritance
  • Class Mixins
  • Component Composition

TLDR; my favorite way is to compose components in small units and use Inputs and Outputs to communicate between components. In order to share logic pieces between components, I like how Mixins can help us avoid some pitfalls from using class inheritance.

In this article, I want to focus more on the relationship between Class Inheritance and Class Mixins, how they differ, and some pitfalls from using Mixins for building components.

The Pitfalls of Class Inheritance

You probably already know why using inheritance is at times very appealing: define some methods and properties once, then use them for every common subclass: wonderful!

On a superficial level, and in some cases, that is actually a good thing. Yet, there are some well-known and documented issues that come with class inheritance. The most important ones, from a component architect’s point of view, are the following:

  • fragile base class — when a change in your base class breaks the derived subclasses
  • it encourages early choices when designing the base class: it makes the design brittle and fragile
  • it breaks encapsulation

In fact, you may have heard the legendary quote from the Gang of Four book:

Favor composition over inheritance

There are several types of components where I see inheritance used pretty often:

  • form fields with common value accessors
  • route components that extend a base-route
  • modal, popups, etc. with common methods (show, hide, etc.)

This article focusses more on business logic rather than purely visual attributes (disabled, animated, etc.). I found that sharing logic between components to be a little bit complex and a mostly misunderstood topic, especially when the framework itself does not provide an official stance regarding the topic, as opposite for example to React.

Typescript Mixins

The concept of Mixins is pretty simple: imagine that instead of having a hierarchy of classes, you instead have a number of very small partial classes. These classes can be combined together and build larger classes with great flexibility.

The way Mixins are created with Typescript is simple: we define a function that takes as argument a class and extend the newly created class with the one passed as the argument.

First, we define the mixins pinMixin and closeMixin that define 1 method each:

function pinMixin(BaseClass) {
  return class extends BaseClass {
     pin() {
        // implementation
     }
  }
}
function closeMixin(BaseClass) {
  return class extends BaseClass {
     close() {
       // implementation
     }
  }
}

We create a Base class that is created by merging the mixins functions, and then we extend the implementation:

const BaseTabMixin = pinMixin(
  closeMixin(class {})
);
class Tab extends BaseTabMixin {}
// Tab now can use the methods `close` and `pin`
Scenario: A Social Media Aggregator App

As an example, I want to build a prototype of a Social Media aggregator application with a feed of posts from the main social media services.

This is a particular example that I faced many years ago as a Junior developer: Babel was released, and ES6 classes were the fancy new thing until they weren’t.

Junior me, a little naively, started creating base classes, extending left and right, and it was exciting. Look how much code I was able to share thanks to them! In the beginning, it’s something you don’t immediately realize: requirements are not fully fleshed-out, and as we all know, new details emerge continuously.

We’re going to see how to build posts components for social media like Facebook, Twitter, Youtube, and Reddit: first, we’ll be using the good old Inheritance.

Afterward, we’ll be refactoring using Composition.

Building a base post component with Inheritance

Let’s proceed and build a BasePost class that shares properties and methods that the derived subclasses will share. As you may already know, social media posts are fairly similar to each other, with subtle differences: they have an author, some content (be it text, a link, or an image), and allow some actions such as liking, sharing, editing, etc.

Our base class PostComponent will have an input (Post object) and will inject a service PostService to which we delegate our actions.

The only common action shared among all the Social posts is delete and therefore we add it to base class so that all the subclasses can inherit the method.

class PostComponent {
  @Input() post: Post;
  constructor(protected service: PostService) {}
  delete() {
     this.post.delete(this.post.id);
  }
}

This is the bare minimum base class we can create. Now, we can proceed and add specific actions.

We know that both Facebook and Twitter allow posts to be liked, but not Twitter nor Youtube; therefore, we create a subclass called LikeablePost:

class LikeablePost extends PostComponent {
    get likes() { 
      return this.post.likes;
    }
    like() {
      this.service.like(this.post.id);
    }

    unlike() {
      this.service.unlike(this.post.id);
    }
}

Both Youtube and Reddit allow posts to be upvoted and downvoted; it makes sense to create a subclass that allows performing such actions:

class VoteablePost extends PostComponent {
    downvote() {
      this.service.downvote(this.post.id);
    }
    
    upvote() {
      this.service.upvote(this.post.id);
    }
}

Facebook and Twitter also have another similarity: the concept of “sharing” as key metadata.

class ShareablePost extends LikeablePost {
    get shares() {
      return this.post.shares;
    }
    share() {
      this.service.share(this.post.id);
    }
}

A similarity shared among Youtube, Facebook and Reddit are that they all allow posts to be edited, unlike Twitter.

This is the first issue we encounter:

  • as the method is not shared by all classes, it would be a mistake to add it to the base class
  • we could implement the method edit for all the subclasses, but that’d be very repetitive

We proceed by implementing TwitterPostComponent

@Component({...})
class TwitterPostComponent extends ShareablePost {}

Let’s take a jump into the future, and Jack gives us horrible news: we can no longer delete tweets! Our class now needs to change, but wait: delete is defined in the base class.

  • if we remove the method from the base class, we will break the other classes
  • if we remove it only from TwitterBaseComponent we will end up breaking the Liskov substitution principle, that means TwitterBaseComponent and PostComponent should be able to be swapped without breaking anything

If it wasn’t clear enough by now, all this was a bad idea.

Enter Composition

Now, we’re going to rewrite all the previous by composing mini-classes instead and using Typescript mixins to create components made of many separate, small classes.

Let’s create the mixins required to create the component TwitterPostComponent: likeMixin, deleteMixin and shareMixin.

Base Class

First of all, we want the mixins to be generic enough to be applied to a variety of components, with one single dependency being the service injected to the component.

export interface PostComponent {
  post: Post;
  service: PostService;
}
likeMixin
// like
function likeMixin<IBasePost extends Constructor<PostComponent>>(
  Base: IBasePost
) {
  return class extends BasePost implements CanLike {
    get likes() {
      return this.post.likes;
    }
    like() {
      return this.service.like(this.post.id);
    }

    unlike() {
      return this.service.unlike(this.post.id);
    }
  };
}
deleteMixin
function deleteMixin<IBasePost extends Constructor<PostComponent>>(
  BasePost: IBasePost
) {
  return class extends BasePost implements CanDelete {
    delete() {
      return this.service.delete(this.post.id);
    }
  };
}
shareMixin
export function shareMixin<IBasePost extends Constructor<PostComponent>>(
  BasePost: IBasePost
) {
  return class extends BasePost implements CanShare {
    shares: number;

    share() {
      return this.service.share(this.post.id);
    }
  };
}
Creating the implementation component: TwitterPostComponent

Once created, we can apply them to the newly created TwitterPostComponent:

const TwitterBase = deleteMixin(
  likeMixin(
    shareMixin(PostComponent)
  )
);

If you prefer to use the applyMixins function described in Typescript’s own documentation, you can do the following:

class TwitterBase extends PostComponent {}

interface TwitterBase extends CanLike, CanDelete, CanShare {}

applyMixins(TwitterBase, [
  shareMixin, 
  likeMixin, 
  deleteMixin
]);

Once created the base component composed with the mixins, we can extend the new component TwitterPostComponent:

@Component({
  selector: 'twitter-post',
  template: `
    <div class="post">
      <div class="post-header">
        {{ post.author }}
      </div>

      <div class="post-content">
        {{ post.content }}
      </div>

      <div class="post-footer">
        <button (click)="like()">Like</button>
        <button (click)="share()">Share</button>
      </div>
    </div>
  `
})
export class TwitterPostComponent extends TwitterBase {
}

In order to remove a delete functionality from the Tweets components, we don’t have to do much — we simply remove the deleteMixin mixin from our class:

const TwitterBase =  likeMixin(
    shareMixin(PostComponent)
  )
);
Pitfalls of using Mixins

Mixins are great, but they’re not an infallible tool. While I would still prefer Mixins to multiple inheritance, it is important to understand the implications of using this technique.

This React blog post provides a great explanation on why Mixins are no longer considered a best practice in React:

  • Mixins create implicit dependencies: mixins that call methods on components, reference a property from the component, or components that need a mixin to work well, are all dependent on each other
  • Mixins start small but grow over time
  • Mixins lead to name clashes

Of course, because of the similarities, these also apply to Typescript mixins used with Angular components.

How to avoid these pitfalls?
  • Try not to apply too many mixins; if you have too many mixins, maybe you should split the component into several components and use component composition with inputs and outputs to communicate between each other
  • Strive to keep them as small as possible
  • Keeps dependencies between mixin/component to a minimum. For example, wherever possible, try not to call a component’s dependencies from the mixin
  • Combine the mixins technique with component composition. Together with the usage of small mixins, you can leverages both techniques to share code and maintain a healthy codebase
Resources

If you need any clarifications, or if you think something is unclear or wrong, do please leave a comment!