AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

The Problem

Have you ever had a case when you need to use a common component but with few changes for specific use cases? could be a table, a list or a card displaying some content.

The first thing that comes to mind is creating a shared component and use @Input for the specifications. In many cases, it might work fine, but over time it could get quite complicated and we might end up with a very large template which contains conditional statements (e.g. *ngIf , *ngSwitchCase ) for each case.

In order to avoid those massive conditional statements and to prevent the need to change our “core code” for each new specification, we will need to somehow decouple parts of the behavior but keep the core logic, or in other words, keep the open-close principle.

Strategy Pattern for the Win 🎉

The strategy pattern is a behavioral design pattern that enables selecting an

algorithm at runtime.

From the book “Design Patterns — Elements of Reusable Object-Oriented Software” by Gang of Four:

Define a family of algorithms, encapsulate each one, and make them

interchangeable. Strategy lets the algorithm vary independently from

clients that use it.

The strategy pattern defines three actors:

Context - This class is the one that uses the ConcreteStrategy algorithm, it gets an algorithm at run time and executes it.

This class is the one that uses the algorithm, it gets an algorithm at run time and executes it. Strategy - an abstract class or interface, it’s the base class of all the ConcreteStrategy classes that provide the algorithm.

an abstract class or interface, it’s the base class of all the classes that provide the algorithm. ConcreteStrategy - Implement the algorithm defined by the Strategy base class.

So for example, let’s say we have three different levels of employees (contractor, fulltime employee, manager) and we want to calculate the company salary (assuming each level has a similar salary).

We will first define our Strategy interface, the Employee , and then implement it for each level.

Now, let’s define a ConcreteStrategy class for each of our employees.

Finally, let’s create a Context that will use those employee classes and calculate the company salary.

Why & When

A possible “when” would be:

When your class has a massive conditional operator that switches between different variants of the same algorithm.

When we want to decouple the behavior and the class that uses the behavior.

When you have a lot of similar classes that only differ in the way they execute some behavior.

The big pro of the strategy pattern is that it allows us to decouple behavior algorithms and to simplify the complexity of our code.

So, if we take a look at our previous example and rewrite it without using the strategy pattern the implementation might be something like:

Strategy Pattern for Components

If we rephrase the definition and use “component” instead of “algorithm” then, strategy pattern for components would be:

Define a family of “presentation / dumb” components, encapsulate each one, that could be selected and used at run time.

This could be achieved using Angular’s awesome DI system.

For example, let's assume we need to create two different “Accordion list” components. The two accordions will look and behave differently, but keep the same core mechanism of an accordion.

Instead of creating two different components that copy the same mechanism, we will use the strategy pattern, so we won’t have to repeat ourselves.

So let’s start by creating our interfaces.

We will also need to add an injection token to our AccordionItem .

As you can see, our accordion is composed of an AccordionWrapper which will act as our context and the AccordionItem that is our strategy.

Let’s start by creating our wrapper:

As you can see, the AccordionComponent uses @ContentChildren decorator in order to inject the AccordionItem from its content, then, it subscribes to itemToggled and open and close the items accordingly.

Now, for our Accordion Items:

We need to create two different accordion items that look and behave differently. To Achieve we will create two components that implement the AccordionItem interface and will provide our ACCORDION_ITEM token in run-time, so our wrapper could inject and use them for the accordion mechanism.

Luckily Angular provides us a way to provide existing instances by using the useExisting property.

Accordion Item 1:

Accordion Item 2:

Finally, we implemented both our context and strategy components, we can now use it in our HTML.