1

This question is about ts inline text interpolation in Angular (not template interpolation for the HTML files).

I couldn't find relevant information about my question, so I'm not sure if this is even possible, is kindly appreciated if you provide source documentation in the case of what I'm asking can't be done.

Currently I can do the following:

export class SomeClass {
    parseString(name: string): string {
        const baseString = `
            Hello ${name}...
        `;
        return baseString;
    }
}

// later... when `parseString` is called I get the following result:
parseString('John');                             // Hello John...
parseString('Mr. Smith');                        // Hello Mr. Smith...

What I want to do is something similar to this:

export class SomeClass {
    parseString(name: string, baseString: string): string {
        return baseString;
    }
}
// later... when `parseString` is called I want to get the following result:
parseString('John', `Hello Mr.${name}`);         // Hello Mr.John...
parseString('Mr. Smith', `Hi ${name}`);          // Hi Mr. Smith...

The syntax I'm proposing is completely wrong because baseString is evaluated before name even exists within its scope or has a value, and so it always returns the string without the variable value: Hello Mr. and Hi .

I don't care about the syntax, any means for taking advantage of text interpolation from a variable would great. Ultimately I would like to be able to do something like this:

/** 
 * The reason that I'm using `of` from `RxJS` is that 
 * I want to show that I want to be able to gather labels from
 * anywhere, let's say a REST API or a service (?)
 **/
of(`Greetings ${name}...`).subscribe(val => {
  const name = 'John';
  console.log(val);                              // Greetings John...
});

Currently, to fulfill all this I just use:

parseString(name: string, baseString: string): string {
    return baseString.replace(/\$\{name\}/g, 'John');
}

But I'm wondering if there's a feature I'm missing with text interpolation.

Edit:

The main difference between this question and the suggested duplicate is that the suggested doesn't care if the back quoted text might be dynamic, here I am trying to be able to "create" those back quoted strings from scratch or based on other variables.

Edit 2:

The suggested duplicate does answer my question here, but works only in ES6 environments.

Edit 3:

Explaining briefly, the ES5 vs ES6 issue. I realized that the suggested duplicate question creates a Function object in runtime, with syntax only available in ES6, which means it'll not work on ES5 environments. On the contrary, Angular TypeScript compilator can create code compatible with ES5 when text interpolation is used.

luiscla27
  • 4,956
  • 37
  • 49

3 Answers3

2

Try this

/**
 * Text interpolation
 * https://www.w3schools.com/js/js_string_templates.asp
 *
 * Example:
 *   const text: string = this.helloI18nTag; // <-- "Hello ${name}, welcome"
 *   const res = StringUtil.interpolate(text, { name: 'John Connor' });
 *   console.log(res);                       // <-- "Hello John Connor, welcome"
 *
 * @param template : string to interpolate
 * @param args     : an object wiht key -> value to replace
 * @returns
 */
public static interpolate(template: string, args: { [key: string]: string }): string {
    if (typeof args !== 'object') {
      return template;
    }
    try {
      return new Function('return `' + template.replace(/\$\{(.+?)\}/g, '${this.$1}') + '`;').call(args);
    } catch (e) {
      // ES6 syntax not supported
    }
    Object.keys(args).forEach((key) => {
      template = template.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), args[key]);
    });
    return template;
}
interpolate('Hello ${name}', {name: 'John'});
luiscla27
  • 4,956
  • 37
  • 49
  • This answer uses `ES6` features when available, but when they're not; it uses the same approach I stated of `"string".replace(/key/g, 'value')` – luiscla27 Nov 26 '21 at 21:05
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 27 '21 at 00:34
0

If all you’re ever doing is concatenating baseString and name, you could just do the following:

parseString(name: string, baseString: string): string {
    return baseString + name;
}

Or if it won’t always be that simple, you could pass a function instead of baseString:

// inside class
parseString(name: string, greeting: (s: string) => string): string {
    return greeting(name);
}

function makeGreeting(name: string) { 
   return `Hi ${name}, I’m Jack.`;
}

console.log(parseString("Bill", makeGreeting)); // “Hi Bill, I’m Jack.”

jdaz
  • 5,964
  • 2
  • 22
  • 34
  • No, I'm not just concatenating strings, the ones from my question are just examples. Mainly, my use-case is to replace keys with their values – luiscla27 Nov 26 '21 at 19:06
  • Please consider in your answer that the string `Hi ${name}, I'm Jack.` might come from outside (a variable). I have an `i18n` file outside the main component. – luiscla27 Nov 26 '21 at 21:10
0

Found an answer for Angular interpolation.

There's a package from @ngx-translate/core called TranslateDefaultParser, the package already cares for the generation of the correct code according to ES5 or ES6 standards. This is how it's used:

import { TranslateDefaultParser } from '@ngx-translate/core';
export class SomeClass {
    ...
    public parser = new TranslateDefaultParser();
    
    parseString(name: string, baseString: string): string {
        return this.parseStringMap(baseString, {name: name});
    }

    parseStringMap(baseString: string, args: { [key: string]: string }): string {
        return this.parser.interpolate(baseString, args);
    }
    ...
}

Later you can use it directly:

this.parseString('John', 'Hello Mr.{{name}}');              // Hello Mr.John
this.parseString('Mr. Smith', 'Hi {{name}}');               // Hi Mr. Smith
this.parseString('Ms. Connor', 'Hello {{name}}, welcome.'); // Hello Ms. Connor, welcome.
this.parseString('Jack', "Hi {{name}}, I'm Bill");          // Hi Jack, I'm Bill

// A bit more complete examples:
// Input string: "Hello {{name}}, today is {{day}}, enjoy... {{something}}"

this.parseStringMap('Hello {{name}}, today is {{day}}, enjoy {{something}}', {
    name: 'John',
    day: 'Wednesday',
    something: 'your stay'
}); // <-- Outputs "Hello John, today is Wednesday, enjoy your stay"


this.parseStringMap('Hello {{name}}, today is {{day}}, enjoy {{something}}', {
    name: 'Everyone!',
    day: 'Friday!',
    something: 'your meal'
}); // <-- Outputs "Hello Everyone!, today is Friday!, enjoy your meal"
...

I'm not going to mark my own answer as correct, because @ngx-translate/core is not an official Angular package, internally; actually does the same as Fazur's answer when catch is thrown. Hopefully there will be an official implementation for this later on.

luiscla27
  • 4,956
  • 37
  • 49