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!

What’s the difference between AngularJS and Angular?

What’s the difference between AngularJS and Angular?

Angular vs Angularjs - key differences, performance, and popularity

AngularJS was released in 2009 and quickly became popular for it's two-way data binding, MVC architecture, and code reusability.

When alternatives like React and Vue delivered the same advantages of AngularJS with better performance, the Angular team decided to completely rewrite the framework.

Each subsequent release of Angular (4,5,6,7,8) has been mostly non-breaking incremental changes. For these reasons, "Angular" now refers to Angular 2+ and "AngularJS" the original.

Key Differences

Here are the key differences between Angular 2+ and AngularJS:

TypeScript

Angular was rewritten using TypeScript. TypeScript is a superset of JavaScript. It compiles to regular vanilla JavaScript but provides syntax for type checking, annotations, and ES6 based extensions.

Since TypeScript is a superset of JavaScript, it needs to be compiled or "transpiled" into ES5 JavaScript so your code still runs in the browser. This requires the use of NodeJS and other build tools for preprocessing TypeScript files.

While using Angular 2+ without TypeScript is possible, the industry standard is to adopt TypeScript as it plays much better with the Angular ecosystem.

MVC vs Component Architecture

AngularJS adheres to the model, view, controller (MVC) software design pattern. Controllers are defined with $scope variables representing an underlying data model. This data model can be updated in both the view and the controller. The view is an HTML file which both displays and dynamically updates $scope variables.

Angular 2+ utilizes more of a component based architecture. Isolated pieces of functionality are defined in components. These components reference their own templates and stylesheets and exist in a hierarchy of other components.

Dependency Injection (DI)

Both AngularJS and Angular use dependency injection. DI allows you to share commonly used functionality across different controllers or components.

In AngularJS, dependencies are injected in controller functions, link functions, and directive definitions.

In Angular, constructor functions, providers, and declarations are used to manage these dependencies.

Angular CLI

Angular 2+ features the Angular CLI: a command line interface for quickly generating Angular components, services, directives, etc. It comes with convenient commands for building your Angular project (compiling TypeScript files and other assets into vanilla js files that run in the browser). It also makes building your project for different environments easier and allows for things like linting, type checking, etc.

AngularJS doesn't have it's own CLI.

Performance

Angular is much faster than AngularJS. In fact, it's said that Angular can be more than 5X faster based on the design of your application.

Popularity

Before the advent of React and Vue, AngularJS was very popular. It offered an elegant solution to the JavaScript SPA with two-way data binding and MVC architecture.

Being able to dynamically update a JavaScript POJO from an HTML template caused a lot of buzz. As a result, alternatives like React and Vue emerged with superior diffing algorithms that left AngularJS in the dust.

Angular fought back with the release of Angular 2 (2016). Today, Angular remains one of the most popular frameworks for UI development.

While AngularJS is still used today, it's popularity has died in favor of more current options like Angular 2+, React, and Vue.

Performance

The problem with AngularJS

Performance is one of the biggest problems with the original AngularJS. This is due to the underlying "magic" behind what originally made AngularJS so popular.

To achieve two-way data binding, AngularJS relies on a digest cycle to keep views in sync with their underlying data models. It works by augmenting all event handlers (clicks, ajax, timeouts) with a process called "dirty checking". Each scoped variable is compared to it's previous value.

If something has changed, the watchers and templates are updated with the new value and the process runs again to see if anything else has changed. In this way, the view is constantly in sync with the data model.

The problem with the AngularJS digest cycle is it's unpredictable. As applications grow, the "checking" process becomes more intensive and can run infinitely with two way data flow.

Angular 2+ to the rescue

To address these issues, the Angular team rewrote the framework with flux architecture in mind. Specifically unidirectional data flow was fundamental to reengineering change detection in Angular.

Now the Angular framework is just as fast as alternatives. When compared to AngularJS, Angular can be more than 5X faster.

The Angular CLI also makes minifying production bundle sizes a breeze, keeping Angular light weight for production.

Advantages of Angular

Angular offers many advantages over the original AngularJS:

Performance:

Angular is up to 5X faster than AngularJS. This is because of a superior diffing algorithm featuring unidirectional data flow and component based architecture.

Server side rendering

Angular offers extensions for rendering your application server side. This is huge for SEO as certain web crawlers can't always scrape async content.

Mobile development

As a framework, Angular makes it possible to develop applications that work on both browsers and native devices like iOS and Android.

Lazy loading

Lazy loading allows you to asynchronously load JavaScript components based on route. This can offer additional performance advantages as code is only imported when it's being used.

Tooling

The tooling provided by TypeScript and the NodeJS ecosystem can't be underestimated. Using the Angular CLI, you can quickly generate Angular components, services, directives etc. without having to manually copy / paste a bunch of boilerplate code.

Additionally, you can more easily build and deploy your project using the CLI.

Should I use Angular or AngularJS?

With the performance advantages, Angular may seem like the best bet moving forward. There is a substantial learning curve to understanding the NodeJS / TypeScript ecosystem and one of the few advantages of AngularJS is that it just runs in the browser.

Using AngularJS makes sense if you have a small application and don't want to bother with learning the ins and outs of NodeJS and TypeScript.

There are also many existing projects out there that already use AngularJS and migrating to newer versions may not justify the cost of learning and rewriting code.

Outside of these cases, adopting "Angular" over the original "AngularJS" is preferred moving forward.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading about Angular

Angular 8 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Complete Node.js Developer Course (3rd Edition)

The Web Developer Bootcamp

Best 50 Angular Interview Questions for Frontend Developers in 2019

How to build a CRUD Web App with Angular 8.0

React vs Angular: An In-depth Comparison

React vs Angular vs Vue.js by Example

Microfrontends — Connecting JavaScript frameworks together (React, Angular, Vue etc)

Building CRUD Mobile App using Ionic 4, Angular 8

How to Build Mobile Apps with Angular, Ionic 4, and Spring Boot

Ionic 4 & Angular Tutorial For Beginners - Crash Course


What is AngularJs 4? Is it a good time to learn Angular Js 4?

Angular 4 is an open-source JS framework used for creating Mobile and web applications in HTML and TypeScript(which is a subset of JS). We can also say that Angular 4 is a significantly modified version of Angular 2. The change what Angular team brought was the TypeScript.

Angular 4 is an open-source JS framework used for creating Mobile and web applications in HTML and TypeScript(which is a subset of JS). We can also say that Angular 4 is a significantly modified version of Angular 2. The change what Angular team brought was the TypeScript.

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