Frontend

What’s new in Angular 15.1 | Frontend Weekly vol. 120

Angular 15.1 was released this week. It adds the long-awaited featur of self closing tags. However, this is not the end of the novelties.

Article cover

1. Angular 15.1

Angular 15.1 have been released this week. Most of the changes and new features are related to the functionalities added in Angular 14 and 15. Without further ado, let’s get to the point.

Self Closing Tags

Frameworks such as Vue or React treat components as purely abstract entities mapped to the DOM structure. If you will take a look at an HTML file generated by one of these frameworks, you will find no trace of components abstraction. Angular takes a completely different approach and assigns a Custom HTML Tag to each component. As a result, when you look at a page generated by Angular, you will immediately see which HTML parts refer to each component.

<!-- The HTML code of a simple application rendered by Angular -->
<!-- You can clearly see how source code is devided into components -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Simple App</title>
    <script src="script.js"></script>
  </head>
  <body>
    <app-root>
      <h2>Welcome to my application!</h2>
      <app-navigation-link>
        <a href="/home">Home</a>
      <app-navigation-link>
      <app-navigation-link>
        <a href="/about">About</a>
      <app-navigation-link>
    <app-root>
  </body>
</html>
<!-- The same application as above but this time rendered with react -->
<!-- It's really hard to say how source code is devided into componets -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Simple App</title>
    <script src="script.js"></script>
  </head>
  <body>
    <div>
      <h2>Welcome to my application!</h2>
      <a href="/home">Home</a>
      <a href="/about">About</a>
    <div>
  </body>
</html>

The Custom HTML Tag specification implies several restrictions. For example, the name of the tag must contain the - character. This is the reason why Angular by default prefixes all selectors with app. This restriction did not wilt out of anywhere – names not containing the - character are reserved for future use in the HTML standard.

According to the HTML standard, only a small group of tags can close itself. The most common are <link />, <br /> or <image /> and you can find the full list here. Unfortunately, Custom HTML tags are not on this list. If we want to comply with the standard, we need to close them with a separate tag. As Angular needs to comply with the standard it falls behind the competition where self-closing tags are rather common (eg. in React you can write <App /> and everything will work fine).

<!-- Below declarations are incorrect -->
<div />                    <!-- ❌ div is not a self closing tag -->
<approot></approot>        <!-- ❌ custom tags must contain - -->
<app-root />               <!-- ❌ custom tags are not self closing -->

<!-- Below declarations are correct -->
<div></div                 <!-- ✅ this is correct syntax -->
<app-root></app-root>      <!-- ✅ this is correct syntax -->

In Angular 15.1, developers have finally managed to find a way to catch up with the competition. The templates we write are not directly sent to the clients. Angular builds its own abstraction over them to handle advanced features like *ngFor or *ngIf. Using this abstraction layer, Angular can replace self-closing tags into a standard-compliant format. This way, the templates we write will not be compliant with the standard, but the HTML sent to the client will be.

<!-- Angular 15.0 -->
<app-button
  text="Click me!"
  variant="filled"
  (onClick)="handleClick($event)"
></app-button>
<app-button
  text="Don't click me!"
  variant="outline"
  (onClick)="handleClick($event)"
></app-button>

<!-- Angular 15.1 -->
<app-button text="Click me!" variant="filled" (onClick)="handleClick($event)" />
<app-button text="Don't click me!" variant="outline" (onClick)="handleClick($event)" />

CanLoad deprecated in favor of CanMatch

Before Angular 15, two guards have been commonly used to restrict access to given routes – CanLoad and CanActivate. If you are using lazy loading, CanLoad can prevent downloading of unreachable pages. The problem is that if the code has already been loaded, CanLoad is not triggered again. Because of that, if we want to restrict access to a given page, we need to also specify CanActivate guard.

const routes: Route[] = [{
  path: '/me',
  canLoad: [() => inject(UserService).isLoggedIn()],
  canActivate: [() => inject(UserService).isLoggedIn()],
  loadChildren: () => import('./user-page/user-page.moudule')
}];

A new Guard called CanMatch have been added to Angular 15. It is called whenever a current navigation path is about to be compared with a route path. This means that it prevents loading unnecessary files but at the same time is called on subsequent navigation to the page.

const routes: Route[] = [{
  path: '/me',
  canMatch: [() => inject(UserService).isLoggedIn()],
  loadChildren: () => import('./user-page/user-page.module')
}];

CanMatch differs from CanLoad and CanActivate in one more aspect. If CanLoad and CanActivate is called, it is the developer’s job to correctly redirect the user. If CanMatch returns false, Angular will continue iterating through the array of routes. This makes it very easy for to host two different pages under the same URL (eg. one for an admin and one for a regular user) or to fallback to the default page.

const routes: Route[] = [
  {
    path: '/me',
    canMatch: [() => inject(UserService).isAdmin()],
    loadComponent: () => import('./admin-page/admin-page.component')
  },
  {
    path: '/me',
    canMatch: [() => inject(UserService).isLoggedIn()],
    loadComponent: () => import('./user-page/user-page.component')
  },
  {
    path: '**',
    loadComponent: () => import('./not-logged-page/not-logged-page.component')
  }
];

In Angular 15.1 CanLoad is marked as deprecated in favor for CanMatch. We don’t know when CanLoad will be removed completely but this is the time to start refactoring your code. It’s also a great opportunity to rewrite you guards in a functional way.

TestBed.runInInjectionContext

The inject() function is a powerful tool added in Angular 1. It can make our code much more readable. It also opened the door for many cool features like functional guards.

function onlyLoggedInGuard(): boolean {
  return inject(UserService).isLoggedIn();
}

const routes: Route[] = [{
  path: '/me',
  canMatch: [onlyLoggedInGuard],
  loadComponent: () => import('./user-page/user-page.component')
}];

Until now, in order to test a guard like the one above, we had to configure a Dependency Injection context for it. The mysterious EnvironmentInjector heavily spoiled the TestBed abstraction, aiming to take away Dependency Injection implementation details from the developers.

describe('onlyLoggedInGuard', () => {
  /* ... */
  it('should not allow not logged in users', () => {
    /* ... */
    expect(
      TestBed.inject(EnvironmentInjector).runInContext(onlyLoggedInGuard)
    ).toEqual(expectedValue);
  })
});

In Angular 15.1, TestBed has been extended with runInInjectionContext, which hides the implementation details from the developers and simplifies configuration a bit.

describe('onlyLoggedInGuard', () => {
  /* ... */
  it('should not allow not logged in users', () => {
    /* ... */
    expect(
      TestBed.runInInjectionContext(onlyLoggedInGuard)
    ).toEqual(expectedValue);
  })
});

isStandalone()

If you’ve ever needed to know whether a component, pipe, or directive has been defined in an old module way or as a Standalone Component, this is possible now.

@Component({
  selector: 'app-standalone-component',
  standalone: true
})
class StandaloneComponent {}

/* ... */
if(isStandalone(StandaloneComponent)) {
  console.log(`StandaloneComponent is a standalone component`)
}

I wondered for a while where I could use the new API. Unfortunately, nothing came to mind. If you have been waiting for this functionality and have some interesting use cases for it, be sure to share them with us on Twitter.

Discover more IT content selected for you
In Vived, you will find articles handpicked by devs. Download the app and read the good stuff!

phone newsletter image

2. State of JS 2022

State of JS is the largest survey on JavaScript. This week the results of last year’s edition have been published. As every year, I recommend everyone to read them for themselves. Traditionally I also share some of my observations below:

  • 70% of respondents were men and only 2.4% of respondents were women. Hopefully, the secret to this disparity lies in the No Answer option marked by 24% of respondents. If not, we have quite a problem here…
  • In the category of frontend frameworks in terms of satisfaction*, Solid is the new king. The old king (Svelte) is still trampling on its back as the difference is only one percentage point (91% vs. 90%). Although developers are rather satisfied with their brand-new frameworks, It’s high time for them to start shortening the gap with the big three.
  • Among frontend frameworks in terms of usage, there are no significant changes – the big three (React, Angular and Vue) still reign supreme. However, it’s worth noting that the Angular and Vue scores declined compared to the previous year. In the case of the first one, this may be related to very low scores in the satisfaction category. In the case of the second one, I would look for the main culprit in Vue 3, which introduced a lot of breaking changes and gave developers a great excuse to migrate to different solutions.
  • Astro has stormed into first place in terms of satisfaction in the server-side rendering frameworks category. This is another argument for taking an interest in this framework and the Dynamic Islands conception.
  • The decline in both satisfaction and usage of webpack may suggest that the technology is slowly giving up in favor of its alternatives. The 98% satisfaction score for Vite is impressive and suggests who will take the place of the long-deserted webpack.
  • Only 27% of respondents spend more time writing JavaScript than TypeScript, and only 11% do not use TypeScript at all. Are there any TypeScript skeptics out there still alive?
This is how I feel now – author

State of JS 2022

3. JavaScript Rising Stars

You have read the State of JS 2022 report and still want some more? No problem – this week another big JavaScript report has been released. The 2022 JavaScript Rising Stars report compiles the GitHub stars growth of various JavaScript tools. You could argue whether GitHub stars are a good source of truth for their popularity. Whatever the answer is, you will find some interesting tools that have escaped your radar there. The report is also worth reading for experts’ comments that follow each report section.

2022 JavaScript Rising Stars

I’ll let the secret out for anyone who doesn’t have the time, strength, or inclination to read the report. This year’s listing was dominated by Bun – a faster drop-in alternative to Node.js and Deno released in the middle of the year. The team behind the runtime has already raised $7M for its further development. In 2023 it’s really worth observing how it will develop.

Discover more IT content selected for you
In Vived, you will find articles handpicked by devs. Download the app and read the good stuff!

phone newsletter image

Bonus: JavaScript Wrapped 2022

If by some miracle you still haven’t had enough recaps and reports, last week I wrote a pretty comprehensive summary of the past year from the perspective of a Frontend Weekly editor. If you haven’t read it yet I warmly invite you to do so.