DEV Community

Connie Leung
Connie Leung

Posted on

New Angular 19 feature - Untagged template literals in expressions

Angular 19.2.0 introduces an untagged template literal in expressions feature where variables can be interpolated with backticks within an Angular expression in a template. The purpose is to facilitate string concatenations within the template of a component.

These cases are supported:

  • Single interpolation
  • Multiple interpolations
  • Interpolation with pipe

The feature is in 19.2.0-next; please test it with a Stackblitz demo.

ng update @angular/cli --next
ng update @angular/core --next
Enter fullscreen mode Exit fullscreen mode

Single Interpolation

@Component({
 selector: 'app-root',
 templateUrl: `./main.component.html`,
 changeDetection: ChangeDetectionStrategy.OnPush,
})
export class App {
 description = `${VERSION.full} - Untagged Template Literals`;
}
Enter fullscreen mode Exit fullscreen mode
// main.component.html
<h1>Version {{ description }}</h1>
Enter fullscreen mode Exit fullscreen mode

Before the untagged template literals feature, the “Version” static text is positioned before the expression.

// main.component.html
<h1>{{ `Version ${description}` }}</h1>
Enter fullscreen mode Exit fullscreen mode

After the untagged template literals feature, the static text, “Version”, and the expression, description, are concatenated into a string. The h1 element renders "Version 19.2.0-next.0 - Untagged Template Literals".

Interpolate class

// app-user.component.css

.color-admin {
   Color: red;
}

.color-user {
   Color: rebeccaPurple;
}

.color-intruder {
   Color: blue
}
Enter fullscreen mode Exit fullscreen mode

Add some classes to change the text color based on user type.

// app-user.component.html

<!-- Template string with pipe -->
@let className = 'color-' + type(); 
<h2 [class]="className">{{ type() | titlecase }} Component</h2>
Enter fullscreen mode Exit fullscreen mode

Before the untagged template literals feature, I declare a temporary variable to derive the class name. The class attribute is an input, and I assign the class variable to it.

// app-user.component.html

<!-- Template string with pipe -->
<h2 class="{{ `color-${type()}` }}">{{ type() | titlecase }} Component</h2>
Enter fullscreen mode Exit fullscreen mode

After the untagged template literal feature, the class name is interpolated within the Angular expression, and two lines are combined into one line.

  • When the user type is Admin, the class is evaluated to 'color-admin', and the text color is red.
  • When the user type is User, the class is evaluated to 'color-user', and the text color is rebeccaPurple.
  • When the user type is Intruder, the class is evaluated to 'color-intruder', and the text color is blue.

Interpolate style

export function getHeaderColor(type: string) {
 if (type === 'admin') {
   return 'red';
 } else if (type === 'intruder') {
   return 'blue';
 } else if (type === 'user') {
   return 'rebeccaPurple';
 }

 return 'black';
}
Enter fullscreen mode Exit fullscreen mode
color = computed(() => getHeaderColor(this.type()));
Enter fullscreen mode Exit fullscreen mode
// app-user.component.html

<!-- Template string with pipe -->
<h2 [style.color]="color()">{{ type() | titlecase }} Component</h2>
Enter fullscreen mode Exit fullscreen mode

Before the untagged template literals feature, I assign the color computed signal to the color attribute of the style.

// app-user.component.html

<!-- Template string with pipe -->
<h2 style="{{ `color: ${color()}` }}">{{ type() | titlecase }} Component</h2>
Enter fullscreen mode Exit fullscreen mode

After the untagged template literal feature, the style attribute and value are interpolated within the Angular expression. Class interpolation is more beneficial than style interpolation, according to my observation.

  • When the user type is Admin, the style is evaluated to "color: red" and the text color is red.
  • When the user type is User, the style is evaluated to "color: rebeccaPurple", and the text color is rebeccaPurple.
  • When the user type is Intruder, the class is evaluated to "color: blue", and the text color is blue.

Multiple interpolations

@Component({
 selector: 'app-root',
 templateUrl: `./main.component.html`,
 changeDetection: ChangeDetectionStrategy.OnPush,
})
export class App {
 description = `${VERSION.full} - Untagged Template Literals`;

 userName = signal('N/A');
 userType = signal<"user" | "admin" | "intruder">('user');

 componentType = computed(() => configs[this.userType()]);
 inputs = computed(() => ({
     permissions: this.componentType().permissions,
     name: this.userName(),
     type: this.userType(),
 }));
}
Enter fullscreen mode Exit fullscreen mode
// main.component.html

@let ct = componentType();
<ng-container [ngComponentOutlet]="ct.type"
 [ngComponentOutletInputs]="inputs()"
 #instance="ngComponentOutlet"
/>

@let permissions = componentInstance?.permissions() ?? [];
@let strPermission = permissions.join(', ');
@let numPermissions = permissions.length;
@let permissionUnit = numPermissions > 1 ? 'permissions' : 'permission';
@let delimiter = numPermissions >= 1 ? ', ' : '';
Multiple Interpolations: {{ numPermissions }} {{ permissionUnit }}{{ delimiter}}{{strPermission} }}
Enter fullscreen mode Exit fullscreen mode

The componentInstance variable is the dynamically rendered component. The permissions variable stores the permission arrays of the rendered component. The strPermission variable concatenates the permissions array into a string. The permissionUnit has the value "permissions" when the user has multiple permissions. When the user has 0 or 1 permission, the value is "permission". A comma is inserted before the permission string when users have multiple permissions.

Before the untagged template literals feature, the static text, "Multiple Interpolations" is positioned before the expressions. All the expressions, numPermissions, permissionUnit, delimiter, and strPermission are interpolated separately.

// main.component.html

@let permissions = componentInstance?.permissions() ?? [];
@let strPermission = permissions.join(', ');
@let numPermissions = permissions.length;
@let permissionUnit = numPermissions > 1 ? 'permissions' : 'permission';
@let delimiter = numPermissions >= 1 ? ', ' : '';
{{ `Multiple Interpolations: ${numPermissions} ${permissionUnit}${delimiter}${strPermission}` }}
Enter fullscreen mode Exit fullscreen mode

After the untagged template literals, these variables are concatenated and interpolated between the curly braces of the Angular expression.

  • When the user type is Admin, the template string is evaluated to "Multiple Interpolations: 4 permissions, create, edit, view, delete".
  • When the user type is User, the template string is evaluated to "Multiple Interpolations: 1 permission, view".
  • When the user type is Intruder, the template string is evaluated to "Multiple Interpolitions: 0 permission".

Interpolation with pipe

@Component({
 selector: 'app-admin',
 templateUrl: `app-user.component.html`,
 imports: [TitleCasePipe],
 changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AdminComponent implements Permission {
 permissions = input.required<string[]>();
 name = input('N/A');
 type = input.required<string>();
}
Enter fullscreen mode Exit fullscreen mode

The AdminComponent component imports the TitleCasePipe pipe to capitalize the permissions and type inputs.

Before the untagged template literals, the expression passes the signal value of type to the titleCase pipe. Then, the static text "Component" follows the expression with a space before it.

After the untagged template literals, the signal value passes to the titleCase pipe and is concatenated with the static text "Component" to form a new string inside the Angular expression.

  • When the user type is Admin, the template string is evaluated to "Admin Component".
  • When the user type is User, the template string is evaluated to "User Component".
  • When the user type is Intruder, the template string is evaluated to "Intruder Component".

The untagged template literals feature is a convenient way to concatenate strings into a string and evaluate it within an Angular expression. Depending on an engineering team's style guide and preferences, engineers can use ES6's template string to concatenate and interpolate strings in the template.

References:

Top comments (1)

Collapse
 
rail_batyrshin_d219c543df profile image
Rail Batyrshin

Thank you for clear explanation, pretty useful feature is coming ;)