During my daily X lurking, I found a post from Younes asking others if they would be open to transitioning from templateUrl to inline templates if Angular allowed importing Typescript structures directly into the template, without having to assign them to class properties.
If (BIG HYPOTHETICAL IF) you could use anything imported or defined in an #Angular component’s TS file inside inline templates, would you switch from using `templateUrl` to inline templates?
Of course, this implies IDE / language service support.
Cf. example below
— Younes (@yjaaidi) October 26, 2024
Well, I already love using inline templates, but it turns out not everyone feels the same. That’s exactly what I want to explore in this article. I’ll also show you how you can boost your productivity by using custom code snippets in your IDE, making it even easier to work with inline templates efficiently.
Also, if you’re not already following Younes, I highly recommend it – he shares some amazing content.
Template file and inline template
Before we dive in, let’s quickly review the options Angular provides for creating components. Thanks to the Angular CLI, generating a component is simple and efficient – it only takes one command. By default, this generates four files:
- .ts file with all logic
- .html file where component’s view lives
- .css/.scss for styling
- .spec.ts for testing
However, we have full control over what and how is being generated by adding extra flags to the command or modifying the config file. We can do various things like modifying change detection strategy, skipping test files, set a selector’s name or use an inline template.
Inline template is when you define the HTML for a component directly inside the Typescript file, using thetemplate
property. So we have 2 options to choose from – either use a separate .html file:
// hello.component.ts
@Component({
standalone: true,
selector: 'app-hello',
templateUrl: './hello.component.html',
})
export class HelloComponent {}
// hello.component.html
<div>Hello world!</div>
or write is as a string in .ts file:
// hello.component.ts
@Component({
standalone: true,
selector: 'app-hello',
template: `<div>Hello world!</div>`,
})
export class HelloComponent {}
The default value for --inline-template
flag is false, which means CLI will generate an .html file for our component’s template. That’s something that we, Angular developers are used to do, which is keeping our template in an external file, to separate the logic between the view. That feels natural, correct and very Angular-ish, right? Of course it does, but I wanna tackle this topic from a different perspective and discover the potential advantages of having an inline template within the .ts file.
„Bad Practice”
While exploring other developers’ opinions, I came across discussions on Reddit and StackOverflow where some suggested that using inline templates is bad practice. In my opinion that’s not true! In fact, inline templates can actually encourage developers to write cleaner and better code.
Simplifying Components with Inline Templates
Inline templates can actually offer significant benefits that are often overlooked. By having both the logic and the template in one place, inline templates can provide immediate context and clarity, making it easier to understand and maintain components.
While large inline templates can bloat the Typescript file, using smaller, focused components mitigates this issue, making your code more modular and maintainable without sacrificing clarity.
In the following example, I’ll show how using inline templates effectively can help simplify your components and improve development flow – while still promoting a clean and organized codebase.
Real example
As an example, in our angular.love repo I found a header component, that had 80 lines of template in .html file. Putting this code into inline template makes .ts file 128 LOC, with 63% being html code. That’s a bit too much, huh?
Instead of having one big file header.component.ts we can break it into smaller parts:
- header.component.ts
- header-logo.component.ts
- header-language.component.ts
- header-mobile-menu.component.ts
- header-hamburger.component.ts
The final template looks as follows:
<header class="bg-al-background/95 z-30 h-20 w-full border-b shadow-xl">
<div
class="mx-auto flex h-full w-full max-w-screen-xl items-center justify-between px-6 py-4 xl:px-0"
>
<al-header-logo />
<div class="flex flex-row items-center">
<al-navigation class="hidden lg:block" />
<al-header-language
[language]="language()"
(languageChange)="languageChange.emit($event)"
/>
<ng-content />
<al-header-hamburger
[isOpened]="showNav()"
(toggleOpen)="toggleNav()"
/>
</div>
</div>
</header>
<al-header-mobile-menu [isOpened]="showNav()" (closed)="toggleNav()" />
To see the original component, check this PR, as the code is too long to be put here https://github.com/HouseOfAngular/angular-love/pull/339/files
From 80 LOC in the template we went down to 24, making the header component 67 LOC total! Of course all of this code hasn’t disappeared, it still exists but in different components. We only distributed its pieces, making the parent component to have a nice descriptive view.
Conclusion
To be fair, this refactor doesn’t change much at first glance. In the UI it looks the same, works the same and we ended up having more files to maintain. But that’s not the clue – this example shows how perspective changes just by having an inline template. I’m pretty sure this component would look this way if inline template was used in the first place. Of course, we could have similar conclusions while still keeping the template in an external file, but inline template gives us an instant feedback as component grows.
You can also say that the purpose of components is to create reusable units of code. The above components are definitely not reusable, are they? Well, I don’t think we have to look at it this way. Components shouldn’t be determined only by their reusability – there are other things to consider. So let me show the list of potential benefits of smaller components:
- Encourage separation of concerns – each component has a single responsibility, which reduces complexity and makes troubleshooting or updates easier.
- Facilitate future reuse – even if you don’t anticipate reuse now, smaller components might prove useful later, either for reuse or for individual testing purposes.
- Higher-level abstraction – smaller components allow you to hide implementation details, making the parent component’s template more concise and easier to reason about.
- Improved focus – breaking down components into smaller, more focused units reduces the mental strain required to understand and maintain the code. This helps you stay organized and feel much better at the end of the day, as you’re not overwhelmed by overly complex components.
Code Review
Keeping templates in separate files forces you to mentally map the links between the component’s logic and its template. This can make it harder to follow the flow of the code during a review, as you need to constantly switch between files to understand how everything fits together. Keeping the template alongside the logic in one place often provides better context and makes the review process more intuitive and efficient. So we can say that inline templates can streamline code reviews and make it faster to iterate on changes.
How to deal with boilerplate
As we switch to a different mental model, we end up creating more components than usual. That’s actually not a problem, since we have the CLI right? Well, I’m not gonna lie – I’m too lazy to use CLI, even if it’s as fast as one click. But modern IDEs offer awesome features that allows to quickly generate any piece of code. In Webstorm for instance, this feature is called Live Templates and essentially prints a predefined snippet by typing a specific abbreviation. We have tons of these already built-in, like a-component
, a-component-inline
. These are great already but I wanted something more tailored to me. I ended up creating my own live template.
VSCode users can achieve the same thing by installing an Angular Snippets extension created by John Papa https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2
If we want them more customised, we can create our own:
{
"Angular Standalone Component":{
"prefix":"comp",
"body":[
"import { ChangeDetectionStrategy, Component } from '@angular/core';",
"",
"@Component({",
"\tstandalone: true,",
"\tselector: '${2:app}${3:${1/[A-Z]/-${0:/downcase}/g}}',",
"\ttemplate: `$0`,",
"\tchangeDetection: ChangeDetectionStrategy.OnPush,",
"})",
"export class ${1:Name}Component {}"
],
"description":"Creates an Angular standalone component"
}
}
This one automatically converts the given Name to a dash-separated, lowercased selector.
This way we can easily create new components by just typing the provided prefix. We can always extend our toolset by creating new snippets for directives, pipes etc.
Are inline templates always a good choice?
Not necessarily. While inline templates have their advantages, they aren’t always the ideal solution. In some cases, templates can grow excessively large, especially when dealing with complex forms. The boilerplate code required for handling value accessors can make it impractical to split the template into smaller parts. In such scenarios, keeping the template in a separate file can make it easier to understand the context and maintain the code.
However, in many situations, especially for smaller or more focused components, inline templates can significantly improve readability and development speed by providing immediate context without needing to switch between files. This direct connection between logic and template can help streamline development, particularly in simpler components where keeping everything in one place enhances efficiency.
Why not both?
There isn’t just one path to follow. There’s nothing wrong with mixing inline and external templates, depending on the situation. Choosing between them should be a personal decision, guided by your preferences or experience. Ultimately, the goal is to use the approach that best suits your project’s needs and makes your development process more efficient.
Sweet spot
So, how do you know when to use an inline template versus a separate template file? Is there a clear breakpoint for when to use an inline template? Is there a line-of-code limit for inline templates? Do you even need them? And won’t mixing both approaches create chaos in the codebase? These are the questions you and your team will need to answer based on your project’s needs.
My personal recommendation is to give inline templates a try whenever possible. It’s a good idea to start by creating components with an inline template from the very beginning. Keeping the template inline is fine as long as the context window makes it easy to understand the component’s flow and logic.
Summary
In this article, we explored the pros and cons of using inline templates in Angular. While inline templates may not always be the best choice for complex or large components, they offer great benefits in terms of readability and efficiency for smaller, focused components. We discussed how breaking down components and keeping templates inline can simplify development, streamline code reviews, and improve context. Ultimately, the choice between inline templates and external files depends on your project’s needs, but it’s worth giving inline templates a shot, especially for smaller components. The key is to find the right balance that works for you and your team.