DEV Community

Cover image for Angular is getting New Template Syntax
Daniel Glejzner for This is Angular

Posted on • Updated on

Angular is getting New Template Syntax

Generated by MidJourney AI

Update 2023–10–07: I have updated this to the final syntax form with @

New Template Syntax, Built-In Control Flow, a farewell to structural directives? Not a lot has been changing in recent years. Angular has been stable for some devs and stagnant for others. Now it’s moving forward at light speed. But where exactly is it headed?

Angular proposes a transition from the current structural directives (NgIf, NgForOf, NgSwitch) to a new built-in syntax. If you’re not already following the RFCs (Request for Comments), please do. In meantime I am going to help you understand what’s coming.

Generated by MidJourney AI

Revamping Control Flow

Angular team aims to replace the existing structural directives (NgIf, NgForOf, and NgSwitch) with a more modern, macro-like syntax. While the structural directives are not going away completely, because the concept is going to stay — this new way of writing your template is going to be the preferred one. It’s backwards compatible and for some time you are going to be able to use both old template style and new one in different files.

Change aims to cover better readability and provide smother way to adapt for wider audience in Frontend world.

Before

The trackByFunction in Angular is a custom function used for optimizing performance when iterating over large collections using *ngFor. In current syntax it takes only a function, in new way it’s going to work just with properties.

Angular will track changes in the collection based on each item’s id, instead of its identity. This is useful when items in the collection have unique ids, improving efficiency when the collection is updated.

    trackByFunction(index, item) {
      return item.id;
    }

    <div *ngFor="let item of items; index as idx; trackBy: trackByFunction">
      Item #{{ idx }}: {{ item.name }}
    </div>
Enter fullscreen mode Exit fullscreen mode

After

@for (item of items; track item.id; let idx = $index, let e = $even) 
{
  Item #{{ idx }}: {{ item.name }}
}
Enter fullscreen mode Exit fullscreen mode

This new syntax is keeping several implicit variables like $index, $first, $last, $even, and $odd within for row views. These are available for use directly, but can also be aliased using the ‘let’ segment. The new syntax also emphasizes using ‘track’ for loops to improve performance and optimizes list diffing by enforcing tracking.

@for (item of items; track item.id) 
{
  {{ item }}
} 
@empty 
{
  There were no items in the list.
}
Enter fullscreen mode Exit fullscreen mode

One crucial feature introduced is the ‘empty’ block which allows developers to display a template when there are no items in the list. Also, Angular is changing how it handles list diffing. Rather than using the customizable IterableDiffers, Angular will provide a new optimized algorithm for better performance.

Generated by MidJourney AI

If-Else Blocks

If you found the original way of working with ng-container & ng-template alongside *ngIf structural directive not intuitive enough — New Control Flow might have an answer for your troubles.

Before (in theory)

    <ng-container *ngIf="cond.expr; else elseBlock">
      Main case was true!
    </ng-container>

    <ng-template #elseBlock>
      <ng-container *ngIf="other.expr; else finalElseBlock">
        Extra case was true!
      </ng-container>

    </ng-template>
    <ng-template #finalElseBlock>
      False case!
    </ng-template>
Enter fullscreen mode Exit fullscreen mode

After

@if (cond.expr) 
{
  Main case was true!
} 
@else if (other.expr) 
{
  Extra case was true!
} 
@else 
{
  False case!
}
Enter fullscreen mode Exit fullscreen mode

Generated by MidJourney AI

Switch Block

The ‘switch’ block takes place of *ngSwitch which. New way of writing is said to bring in substantial benefits like enhanced template type-checking and no need for container elements to hold condition expressions. Here’s a quick peek at how it would look:

@switch (condition) 
{
  @case (caseA) 
  {
    Case A.
  }
  @case (caseB) 
  {
    Case B.
  }
  @default 
  {
    Default case.
  }
}
Enter fullscreen mode Exit fullscreen mode

Generated by MidJourney AI

What About Migration?

Migration to the new syntax is promised be relatively smooth. Angular team is working on an automated migration schematic to convert from the old to the new syntax. However, developers might need to be cautious about any custom diffing algorithm used in their applications as it could affect the new ‘for’ directive’s behavior.

Generated by MidJourney AI

Future Opportunities

The Angular team envisions extending this new syntax to accommodate more JS loop flavors, including async iteration, and for-in loops. Potential future improvements also include virtual scrolling and destructuring support.

There is also another RFC showing new feature in action — this feature is based new Built-In Control Flow. Checkout RFC for Deferred Loading

Generated by MidJourney AI

FAQ Recap

Let’s quickly address some common concerns developers might have about this new control flow syntax:

  • Existing structural directives: The current structural directives (NgIf, etc) will continue to work. However, Angular will strongly encourage developers to switch to the new syntax.

  • Structural directive concept: It will not be removed and remains an essential feature in Angular.

  • Syntax highlighting: Yes, the Angular Language Service will highlight keywords and expressions within the new control flow blocks.

  • Effect on query results: The new control flow will not affect query results.

  • Need to import new control flow: No, it will be built into the template language and automatically available to all components.

  • Performance: The new control flow might offer marginal improvements, particularly for ‘for’ and diffing.

  • Custom block groups and directives: At present, the new syntax doesn’t support libraries to define custom block groups, and you can’t add directives to the new control flow blocks.

Generated by MidJourney AI

What do you think?

This proposed syntax change is going to affect the way you write your templates. Do you think it’s needed? Is this change going to improve your DX and bring new devs aboard that prevously preferred to use different frameworks?

My main concerns lie within the transition period. We have a significant change in reactive primitive with Signals and now new Template Syntax is being introduced. It’s definitely coming together to paint a bigger picture that hasn’t been unveiled to us yet. For some time Angular devs are going to get many options to handle reactivity: RxJS, Signals, Promises, Change Detection (zone.js) + 2 ways of writing templates.

It will definitely take a long time to fully transition to the new vision and it’s not like you can speed this process up. Now the question would be, is the cost of these advancements worth what we are getting in the end?

Make sure to let me know and have a discussion!


I hope you liked my article!

If you did you might also like what I am doing on Twitter. I am hosting live Twitter Spaces about Angular with GDEs & industry experts! You can participate live, ask your questions or watch replays in a form of short clips :)

If you are interested drop me a follow on Twitter @DanielGlejzner — would mean a lot :). Thank You!

Oh no! My Coffee cup is empty :( …
… if you want to refill go ahead :) https://ko-fi.com/danielglejzner

Top comments (41)

Collapse
 
nicholaseden profile image
nicholas-eden

Am I the only one that is disgusted by this change? I liked Angular because you write html. It's easy to learn and is visually appealing. This is an ugly mess.

I'm sick of the Angular team trying to reinvent the entire framework every two years. It makes maintenance incredibly difficult and pushes people towards lighter and more stable frameworks. If people like this syntax then let them switch to a tool that uses it.

I don't mean to sound like the grumpy old guy, but the rapid changes in frontend tools makes my just want to use underscore and plain JS/TS.

Collapse
 
oz profile image
Evgeniy OZ

makes my just want to use underscore and plain JS/TS

You really should try it. Every web developer should, I'm not joking. After that, you will understand much better how frameworks work and what value they bring.

Collapse
 
nicholaseden profile image
nicholas-eden

I started doing frontend work before Angular was even a thing, you don't gotta tell me that. But modern frameworks have gone off the deep end and frequently end up being more of a burden than a help: Constant breaking changes and changes in convention, bloated dependencies, neglect for debugability, etc. At my last company every time we upgraded Angular it was a full month endeavor to coordinate updates across 5+ teams, and for what? Slightly smaller builds and some features we didn't use? I deal with React currently, which has its own issues.

I'm not saying don't use them, I just want a bit more stability in the framework space. I'd love to get to pick the framework and choose one that promises to stay out of the way. The big names have so many engineers who want to make things "better" and end up causing more problems.

Thread Thread
 
oz profile image
Evgeniy OZ

Try Lit - as far as I know, it's the most stable web framework.

The change, discussed in this article, is not breaking, though.

Thread Thread
 
nicholaseden profile image
nicholas-eden

Yeah, its more of the style change that bugs me. I like the style of html tags and templates, it looks cleaner imo because it fits the style of the rest of the template. This brings back mustache style brackets where Angular helped us reduce, its ugly imo.

This proposal would be a great optional plugin, it doesn't need to be the default. It seems like leadership changed and so did the vision.

I'll look at Lit though.

Thread Thread
 
oxharris profile image
Oxford Harrison

Talking about stability:

Syntax is a moving target on the frontend and interestingly there is a destination (which I talked about in my article), and until we get there, syntax can't be stable!

You'd realise how much we can skip if we could just use plain JS for rendering logic instead of re-inventing a new language (every new turn) that maps to the same JS constructs! (See also Eckehard's comment.)

Thread Thread
 
oz profile image
Evgeniy OZ

You can do this - stop using Angular and start using vanilla JS.

Collapse
 
florianrappl profile image
Florian Rappl

This is unfortunately not correct. The original syntax also does not parse as HTML (well anything parses as HTML, however, it will not come out as you've entered).

I also was under the wrong assumption that angular calling the template files .html and essentially using attributes is for that reason - but the parser and it's logic is still custom (and needs to be).

Collapse
 
nermin_karapandzic profile image
Nermin Karapandzic

I haven't read the full story, but this doesn't seem like it's going to force you use the new syntax, the old syntax is still just directives, so unless they're removing directives completely then you should have backwards compatibility.

Collapse
 
nicholaseden profile image
nicholas-eden

I read the post and its not entirely clear. In the title its called an alternative syntax, but in the body they describe building migration tooling. That implies they wouldn't support both in perpetuity.

Thread Thread
 
danielglejzner profile image
Daniel Glejzner

Both are going to be supported for a period of time :)

Collapse
 
spock123 profile image
Lars Rye Jeppesen

I love these changes.
I just did a new standalone app, using only Signals. That, and the new inject() functional stuff is so so so awesome to work with.

I cannot wait to be able to ditch ZoneJS.
This new syntax is basically a copy of the Svelte-syntax, which makes it easier for some new developers to onboard, I guess.

Collapse
 
rd88a5d657 profile image
rd • Edited

there is "Alternatives Considered". please check source link. i would like to see this syntax instead

<ng:if cond="expr">
  <ng:then>
    True case
  </ng:then>
  <ng:else>
    False case
  </ng:else>
</ng:if>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nicholaseden profile image
nicholas-eden

Yeah, I read over the entire post. That syntax looks fine imo. I don't mind things taking a few more lines if it is consistent and easy to read. This is **much **easier to read than than trying to parse over Mustache style syntax they are trying to push. We moved away from that for a good reason, this is regression imo.

Thread Thread
 
spock123 profile image
Lars Rye Jeppesen

Svelte uses it and it rocks

Collapse
 
ant_f_dev profile image
Anthony Fung

I'm not repulsed by it, but I'm not particularly fond of it either. I'm just dreading the inevitable syntax migration when the current version becomes deprecated.

Collapse
 
spock123 profile image
Lars Rye Jeppesen

I love it, to be honest it's amazing with all the new changes. I didn't think much of them until I made a new app, standalone, only using signals, and inject() to make functional ngrx effects etc. Such an amazing experience that I cannot go back now.

Thread Thread
 
ant_f_dev profile image
Anthony Fung

That's fair enough - new projects are one thing, but having to maintain an existing project with 1,000s of files is another. I'm guessing it'll be easier than AngularJS -> Angular 2, but I suspect it won't be as simple as running a migration script either.

Thanks for sharing that the overall development experience is smoother.

Collapse
 
stephenwhitmore profile image
Stephen Whitmore

I personally really like this. Anything to make code easier to read is always a win

Collapse
 
dreitzner profile image
Domenik Reitzner

As a Svelte dev, it is nice to see the influence here ☺️
The readability argument is a strong one, especially for those among us who are more senior who tend to read a lot of code.

Collapse
 
spock123 profile image
Lars Rye Jeppesen

Yep, love this

Collapse
 
yousufamre profile image
YousufAmre

This seems much similar to templating using handlebars.js

Collapse
 
ignace profile image
Ignace Maes

It's great to see frameworks take over the good parts from each other. Ember.js has been using this type of control flow too for years and I personally much prefer it over inline attributes.

Collapse
 
specialdoom profile image
specialdoom

Feels like Svelte it's becoming influential. But that's a good thing right?

Collapse
 
cropwatchdevelopment profile image
CropWatch

Its a nice start! But before moving back to Angular i want to see svelte like 3rd party js library level compatability

Collapse
 
spock123 profile image
Lars Rye Jeppesen

You really should check it out, Signals and inject() makes it sooooooo nice

Collapse
 
philipjohnbasile profile image
Philip John Basile

Really cool post. Makes me want to check bleeding edge Angular.

Collapse
 
spock123 profile image
Lars Rye Jeppesen

It's awesome. Standalone-app with no modules, inject() function makes you able to easy inject Services into functions, etc etc. Wondeful

Collapse
 
krivanek06 profile image
Eduard Krivanek

The angular plugin for VScode will need some additional schematics, such that when I start typing "if", it will generate the code block for me, nonetheless I like the new syntax. Seems like current FE starts to become more and more similar to each other.

Collapse
 
draylegend profile image
Vladimir Drayling • Edited

I prefer to use a global directive to track ids in *ngFor:

@Directive(...)
export class TrackByDirective {
  ...
}
Enter fullscreen mode Exit fullscreen mode

now I don't need to use trackBy for every *ngFor

<app-user-list>
  <app-user-item *ngFor="let u of users()" />
</app-user-list>
Enter fullscreen mode Exit fullscreen mode

The directive is responsible to track by id.

How can I track ids globylly with the new syntax without providing it for every loop?

Collapse
 
tecknock profile image
marcoalesan

I think @if is a good tool but it does not replace *ngIf

I hope they don't remove *ngIf and let us use them both as needed

*ngIf is good for simple use

<button *ngIf="!isProtected" (click)='remove(element)'>delete</button>
Enter fullscreen mode Exit fullscreen mode

while @if is better with complex use

@if (isProtected) {
  <span class='text-red'>
    you don't have permission to remove {{element.name}}
  </span>
  <button class='btn-blue' (click)='reqPerm(element)'>request permission</button>
} @else {
  <button (click)='remove(element)'>delete</button>
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
rytis profile image
Rytis

I haven't worked with Angular since v4, but I'm excited to see this change. I've always liked Angular, because it had the most similarities with backend code - dependency injections, etc. I hate XML though, and I think that tag attributes are not easily readable, so any move away from that is welcome. For me Twig was the perfect templating language

Collapse
 
spock123 profile image
Lars Rye Jeppesen

Dependency inject in Angular is now a breeze, with the inject() function you can inject services into your own functions, not needed to make classes or anything. Awesome

Collapse
 
bretbernhoft profile image
Bret Bernhoft

This is interesting. I've been using Angular for a while now, and have become rather acquainted with how it currently works. With that said, I'm curious to see how these changes are adopted by the global Angular community.

Collapse
 
ant_f_dev profile image
Anthony Fung

Thanks for the summary!

Personally, I don't find the new syntax significantly easier to read, but maybe it's because I got used to the current syntax.

I wonder what's driving the change. Maybe it wants to be like other frameworks for more popularity; or maybe it's because Google just likes changing its tech often.

Collapse
 
mikec711g profile image
Michael Casile

I like the new options. Glad I have a preview at this point in my dev.

Collapse
 
pterpmnta profile image
Pedro Pimienta M.

Angular features are getting really cool. thanks for the post.