In an Angular 1 application we have been creating for one of our customers we used the $interpolate service to build a simple templating engine. The user was able to create snippets with placeholders within the web application to use these message fragments to compose an email to reply to a support request.

In Angular 2 there is no such service like $interpolate - but that is not a problem because we have got abstract syntax tree (AST) parsers to build our own interpolation library. Let’s build a component that takes a format string (with placeholders) and an object with properties to be used for replacement of the placeholders. The usage looks like this:

// returns 'Hello World!'
interpolation.interpolate('Hello {{place.holder}}!', { place: { holder: 'World!'}});

At first we need to inject the parser from Angular 2 and we need to create a lookup to cache our interpolations.

constructor(parser: Parser) {
    this._parser = parser;
    this._textInterpolations = new Map<string, TextInterpolation>();

The class TextInterpolation is just a container for saving the parts of a format string. To get the interpolated string we need to call the function interpolate. The example from above will have 2 parts:

  • String 'Hello '
  • Property getter for {{place.holder}}
class TextInterpolation {
    private _interpolationFunctions: ((ctx: any)=>any)[];

    constructor(parts: ((ctx: any) => any)[]) {
        this._interpolationFunctions = parts;

    public interpolate(ctx: any): string {
        return => f(ctx)).join('');

Before we can create our TextInterpolation we need to parse the format string to get an AST.

let ast = this._parser.parseInterpolation(text, null);

if (!ast) {
    return null;

if (ast.ast instanceof Interpolation) {
    textInterpolation = this.buildTextInterpolation( ast.ast);
} else {
    throw new Error(`The provided text is not a valid interpolation. Provided type ${ast.ast.constructor && ast.ast.constructor['name']}`);

The AST of type Interpolation has 2 collections, one with strings and the other with expressions. Our interpolation service should support property-accessors only, i.e. no method calls or other operators.

private buildTextInterpolation(interpolation: Interpolation): TextInterpolation {
    let parts: ((ctx: any) => any)[] = [];

    for (let i = 0; i < interpolation.strings.length; i++) {
        let string = interpolation.strings[i];

        if (string.length > 0) {
            parts.push(ctx => string);

        if (i < interpolation.expressions.length) {
            let exp = interpolation.expressions[i];

            if (exp instanceof PropertyRead) {
                var getter = this.buildPropertyGetter(exp);
            } else {
                throw new Error(`Expression of type ${exp.constructor && exp.constructor.name1} is not supported.`);

    return new TextInterpolation(parts);

The strings don’t need any special handling but the property getters do. The first part of the special handling happens in the method buildPropertyGetter that fetches the value of the property (and the sub property) of an object.

private buildPropertyGetter(exp: PropertyRead): ((ctx: any) => any) {
    var getter: ((ctx: any) => any);

    if (exp.receiver instanceof PropertyRead) {
        getter = this.buildPropertyGetter(exp.receiver);
    } else if (!(exp.receiver instanceof ImplicitReceiver)) {
        throw new Error(`Expression of type ${exp.receiver.constructor && (exp.receiver)} is not supported.`);

    if (getter) {
        let innerGetter = getter;
        getter = ctx => {
            ctx = innerGetter(ctx);
            return ctx && exp.getter(ctx);
    } else {
        getter = <(ctx: any)=>any>exp.getter;

    return ctx => ctx && getter(ctx);

The second part of the special handling is done in addValueFormatter that returns an empty string when the value returned by the property getter is null or undefined because these values are not formatted to an empty string but to strings 'null' and 'undefined', respectively.

private addValueFormatter(getter: ((ctx: any) => any)): ((ctx: any) => any) {
    return ctx => {
        var value = getter(ctx);

        if (value === null || _.isUndefined(value)) {
            value = '';

        return value;

The interpolation service including unit tests can be found on GitHub: angular2-interpolation

Related Articles

 | Konstantin Denerz

In this article you will see how to enrich your test suite with functional and visual end-to-end tests for your Angular applications. Setting the Scene In the previous article, Visual Testing Of Angular Components With Storybook & Chromatic, I described how to use visual tests to…

Read article
 | Konstantin Denerz

In a previous article, From paper sketch to a running application - Adobe XD, Storybook & Angular in action, I described the basics of writing Storybook stories and Angular components. In this follow-up, you will learn to enhance the Storybook workflow through visual testing to…

Read article
 | Thomas Hilzendegen

"Bitte prüfen Sie, ob eine Verbindung zum Internet vorhanden ist" - ein Satz, den wir alle kennen, wenn wir eine Anwendung nutzen wollen, die nicht offline verfügbar ist. In diesem Artikel zeigen wir, wie man eine bestehende (Angular-) Anwendung und ihre Daten offline nehmen kann…

Read article