Free Resources
Easy Angular Unit Testing in NgRx
Recommended Articles
SSR Finally a First-Class Citizen in Angular?
I am sure you've already heard about server-side rendering (SSR). Generally, it's a technique used to deliver the HTML content generated by the server to the client. The client-side JavaScript framework then hydrates that HTML content to provide a fully interactive application. SSR provides numerous benefits if used correctly, including improved initial load times, better content indexing by search engines, and improved performance on lower-tier devices. Driven by the need for faster, more efficient web applications, with the rise in popularity of Node.js and, thus, server-side JavaScript, SSR has become a de facto standard in modern web development frameworks. At first, SSR was being provided as a part of meta-frameworks like Next.js, Nuxt, or Analog.js. Then, React introduced React Server Components, which marked a significant milestone in the way we think about front-end development. We think of front-end frameworks as being effectively full-stack, and a purely SPA approach is falling out of favor. However, I'm willing to bet that if you're a developer focused mostly on Angular, you may not have heard as much about SSR as your React counterparts. For a long time, Angular has been mostly focused on the SPA approach, and while we had Angular Universal or Analog.js, it was not as widely adopted. And that makes sense, given that Angular was widely regarded as best suited for complex, interactive applications, such as SaaS products or internal company tools, where SEO and initial load times were not as critical as they are for consumer-facing applications. Nevertheless, we have been blessed with the so-called Angular Renaissance, which has, among other things, brought SSR to the forefront of Angular development with the @angular/ssr package. Let's look at what Angular SSR is, how it works, and how you can get started. @angular/ssr The @angular/ssr package is not new; it was written from scratch. It's still Angular Universal. However, it's been pulled into the Angular CLI repository, and the appropriate schematics have been added to make it easier to use. What that means is that you can now add SSR to your Angular application with a single command: ` and you can generate a new SSR application with: ` And to make SSR a first-class citizen, the Angular team is planning to make it the default in the next version. But surely, it's not only rebranding and some CLI commands, you may say. And you're right. You may remember that hydration in Angular Universal has not been optimal as it was destructive and required essentially a complete re-render of the application, thus pretty much negating the benefits of SSR. With the introduction of non-destructive hydration in Angular 16 and its stability in Angular 17, the Angular team has taken a significant step forward in making SSR a first-class citizen. How to Make an SSR-capable Angular Application Let's look at how to use Angular SSR in practice by creating a simple SSR app. First, we need to create a new Angular application with SSR support: ` That will generate a few server-specific files: - server.ts, which is an Express server configured to handle Angular SSR requests by default but can also be extended to handle API routes, or anything else that Express can handle. - src/app/app.config.server.ts provides configuration for the server application similar to your app.config.ts, which defines the configuration for your client application. Both export an instance of ApplicationConfig - src/main.server.ts bootstraps the server application in a similar way to what main.ts does for your client app by calling the bootstrapApplication function. You should have an SSR application ready just like that. You can verify that by running ng serve. The terminal output should show you information about the server bundles generated, and you should be able to see the default app when going to http://localhost:4200. Open the Developer Tools and check the console to see that hydration is enabled. You'll see a message with hydration-related stats, such as the number of components and nodes hydrated. Speaking of hydration... Client Hydration If you open the src/app/app.config.ts in your new SSR application, you'll notice one interesting provider: provideClientHydration(). This provider is crucial for enabling hydration - a process that restores the server-side rendered application on the client. Hydration improves application performance by avoiding extra work to re-create DOM nodes. Angular tries to match existing DOM elements to the application's structure at runtime and reuses DOM nodes when possible. This results in a performance improvement measured using Core Web Vitals statistics, such as reducing the Interaction To Next Paint (INP), Largest Contentful Paint (LCP), and Cumulative Layout Shift (CLS). Improving these metrics also positively impacts SEO performance. Without hydration enabled, server-side rendered Angular applications will destroy and re-render the application's DOM, which may result in visible UI flicker, negatively impact Core Web Vitals like LCP, and cause layout shifts. Enabling hydration allows the existing DOM to be reused and prevents flickering. Event Replay and Improved Debugging With Angular 18, event replay, a feature that significantly enhances the SSR experience, came into developer preview. Event replay captures user interactions during the SSR phase and replays them once the application is hydrated. This ensures that user actions, such as adding items to a cart, are not lost during the hydration process, providing a seamless user experience even on slow networks. To enable event replay, you can add withEventReplay to your provideClientHydration call: ` Furthermore, Angular 18 takes another step towards making SSR a first-class citizen by enhancing the debugging experience with Angular DevTools. Now, DevTools include overlays and detailed error breakdowns, empowering developers to visualize and troubleshoot hydration issues directly in the browser. Constraints However, hydration imposes a few constraints on your application that are not present without hydration enabled. Your application must have the same generated DOM structure on both the server and the client. The hydration process expects the DOM tree to have the same structure in both environments, including whitespaces and comment nodes produced during server-side rendering. Direct DOM Manipulation The fact that server and client DOM structures must match means that components that manipulate the DOM using native DOM APIs or use innerHTML or outerHTML will encounter hydration errors. The errors are thrown because Angular is unaware of these changes and cannot resolve them during hydration. Examples include accessing the document, querying for specific elements, and injecting additional nodes using appendChild. Detaching DOM nodes and moving them to other locations will also cause errors. Direct DOM manipulation is generally considered bad practice in Angular, so I recommend refactoring your components to avoid it regardless of hydration. However, in warranted cases where refactoring is not feasible, you can use the ngSkipHydration attribute until a hydration-friendly solution can be implemented. You can use it directly in a template: ` or as a host binding: ` > Note: The ngSkipHydration attribute forces Angular to skip hydrating the entire component and its children. So use this attribute carefully, as, for example, applying it to your root application component would effectively disable hydration for the entire application. Valid HTML Structure Another constraint is that invalid HTML structures in component templates can result in DOM mismatch errors during hydration. Common issues include: - without a - inside a - inside an - inside another Again, if you have an invalid HTML, you should fix this regardless of hydration. If you are unsure about your HTML's validity, you can use the W3 validator to check it. Zoneless apps or custom Zone.js Hydration relies on a signal from Zone.js when it becomes stable. Custom or "noop" Zone.js implementations may thus disrupt the timing of this event, triggering the serialization or cleanup process too early or too late. This configuration is not yet fully supported. While Zoneless is already available in developer preview and we could assume this issue will be resolved when it lands as stable, there is no explicit ETA mentioned in the official roadmap. I18n One of the biggest blockers for adoption of client hydration was the lack of support for internationalization with hydration. By default, Angular 17 skipped hydration for components that used i18n blocks, re-rendering them from scratch. In Angular 18, however, hydration for i18n blocks got into developer preview. To enable hydration for i18n blocks, you can add withI18nSupport to your provideClientHydration call. ` Third-Party Libraries with DOM Manipulation The last thing to consider is third-party libraries that manipulate DOM, such as D3 charts. They could cause DOM mismatch errors when hydration is enabled. You can use the ngSkipHydration attribute to skip hydration for components rendered using these libraries. Conclusion The Angular team has been doing great work to make the framework more modern and competitive, hearing the community feedback and adjusting the roadmap accordingly. And SSR is an excellent example of that. With the introduction of non-destructive hydration in Angular 16 and making it stable in Angular 17, the Angular team has taken a significant step forward in making SSR a first-class citizen in Angular. The @angular/ssr package is now part of the Angular CLI, making adding SSR to your Angular application easier. The Angular team plans to make SSR the default in the next version, further solidifying its position in the Angular ecosystem. However, as with any new feature, there are some constraints and best practices to follow when using SSR with Angular. You should be aware of hydration's limitations and ensure your application is compatible with it. However, if you follow the guidelines provided by the Angular team, you should be able to take advantage of the benefits of SSR without any significant issues....
Jun 26, 2024
8 mins
Angular 18 Announced: Zoneless Change Detection and More
Angular 18 Announced: Zoneless Change Detection and More Angular 18 has officially landed, and yet again, the Angular team has proven that they are listening to the community and are committed to continuing the framework's renaissance. The release polishes existing features, addresses common developer requests, and introduces experimental zoneless change detection. Let's examine the major updates and enhancements that Angular 18 offers. 1. Zoneless Angular One of the most exciting features of Angular 18 is the introduction of experimental support for zoneless change detection. Historically, Zone.js has been responsible for triggering Angular's change detection. However, this approach has downsides, especially regarding performance and debugging complexity. The Angular team has been working towards making Zone.js optional, and Angular 18 marks a significant milestone in this journey. Key Features of Zoneless Angular 1. Hybrid Change Detection: In Angular 18, a new hybrid change detection mode is enabled by default. This mode allows Angular to listen to changes from signals and other notifications regarding changes that occur either inside an Angular zone or not. That effectively means you can write (library) code that works regardless of whether Zone.js is being used, paving the way to fully zoneless apps without compromising backward compatibility. For new applications, Angular enables zone coalescing by default, which removes the number of change detection cycles and improves performance. For existing applications, you can enable zone coalescing by configuring your NgZone provider in bootstrapApplication: ` 2. Experimental API for Zoneless Mode: Angular 18 introduces an experimental API to disable Zone.js entirely. This API allows developers to run applications in a fully zoneless mode, paving the way for improved performance and simpler debugging. The zoneless change detection requires an entirely new scheduler which relies on notifications from the Angular core APIs, such as ChangeDetectorRef.markForCheck (called automatically by the AsyncPipe), ComponentRef.setInput, signal updates, host listener updates, or attaching a view that was marked dirty. 3. Improved Composability and Interoperability: Zoneless change detection enhances composability for micro-frontends and interoperability with other frameworks. It also offers faster initial render and runtime, smaller bundle sizes, more readable stack traces, and simpler debugging. How to Try Zoneless Angular To experiment with zoneless change detection, you can add the provideExperimentalZonelessChangeDetection provider to your application bootstrap: ` After adding the provider, remove Zone.js from your polyfills in angular.json. You can read more about the experimental zoneless change detection in the official documentation. By the way, angular.dev is now considered the official home for Angular developers! 2. Server-side Rendering and Hydration Enhancements A feature I'm particularly excited about is the improvements to Angular's server-side rendering (SSR) and hydration in terms of developer experience and debugging: 1. Enhanced DevTools Support: Angular DevTools now includes overlays and detailed error breakdowns to help visualize and debug hydration issues directly in the browser. This refreshing focus on developer experience shows that the Angular team wants to make the framework more approachable and user-friendly. 2. Hydration Support for Angular Material: All Angular Material components now support client hydration and are no longer skipped, which enhances performance and user experience. 3. Event Replay: Available in developer preview, event replay captures user interactions during SSR and replays them once the application is hydrated, ensuring a seamless user experience before complete hydration. It is powered by the same library as Google Search. 4. i18n Hydration Support: Up to v17, Angular skipped hydration for components with i18n blocks. From v18, hydration support for i18n blocks is in developer preview, allowing developers to use client hydration in internationalized applications. 3. Stable Material Design 3 After introducing experimental support for Material Design 3 in Angular 17, Angular 18 now includes stable support. The key features of Material Design 3 in Angular 18 include: 1. Simplified Theme Styles: Based on CSS variables, the new theming styles offer more granular customization and a flexible API for applying color variants to components. 2. Theming Generation Schematics: Using the Ng CLI, you can generate Material 3 themes. 3. Sass APIs: New Sass APIs allow developers to read colors, typography, and other properties from the Material 3 theme, making it easier to create custom components. How to use Material Design 3 in Angular 18 To use Material Design 3 in Angular 18, you can define a theme in your application's styles.scss file using the mat.defineTheme function: ` Or generate a Material 3 theme using the Ng CLI: ` You can then apply the theme to your application using the mat.theme mixin: ` Head to the official guide for a more detailed guide. You'll also notice they have refreshed the docs with the new themes and further documentation. 4. Signal-Based APIs The path to fully signal-based components includes new signal inputs, model inputs, and signal query APIs. We already wrote about them as they were in developer-preview in v17, but they have been further refined in v18. These APIs offer a type-safe, reactive way to manage state changes and interactions within components: 1. Signal Input API: Signal inputs allow values to be bound from parent to child components. Those values are exposed using a signal and can change during the component's life cycle. 2. Model Input API: Model inputs are a special input type that enables a component to propagate new values back to the parent component. That allows developers to keep the parent component in sync with the child component with two-way binding. 3. Signal Query API: This was a particularly requested feature from the community. The signal query APIs work the same way as ViewChild and ContentChild under the hood, but they return signals, providing more predictable timing and type safety. 5. Fallback Content For ng-content A very requested feature from the community, Angular 18 introduces a new ng-content directive that allows developers to define fallback content when no content is projected into a component. This feature is particularly useful for creating reusable components with default content. Here's an example of using the new ng-content directive. Using the following component ` like this ` will render Howdy World. 6. Other Improvements In addition to the major updates mentioned above, Angular 18 also includes several other improvements and updates: 1. TypeScript 5.4: Angular 18 now supports TypeScript 5.4, which lets you take advantage of new features such as preserved narrowing in closures following last assignments. 2. Global Observable in Angular Forms: Angular 18 introduces a global events observable in Angular forms, which allows you to track all changes around any abstract control and its children, including the touched or dirty in a single observable. Here's an example of how you can use the global observable: ` 3. Stable Deferrable views: Deferrable views are now stable in Angular 18. 4. Stable Control Flow: The built-in control flow is now stable in Angular 18! It is more performant than its predecessor. It also received improved type checking, including guardrails for certain performance-related anti-patterns. 5. Route Redirects as Functions: For added flexibility in managing redirects, Angular v18 now lets you use a function for redirectTo that returns a string, which allows you to create more sophisticated redirection logic based on runtime conditions. For example: ` Conclusion Angular 18 is a significant release that brings many new features, enhancements, and experimental APIs to the Angular ecosystem. The introduction of zoneless change detection, improvements to server-side rendering and hydration, stable Material Design 3 support, signal-based APIs, and fallback content for ng-content are just a few of the highlights of this release. The Angular team has again demonstrated its commitment to improving the framework's developer experience, performance, and flexibility. It also demonstrated a clear vision for Angular's future. If you're curious about what's next, you can check out the Angular roadmap....
May 29, 2024
3 mins