New Core Web Vitals and How They Work
A guide to INP, the new Core Web Vital coming in March 2024. The what, why and how....
Feb 14, 2024
Testing Accessibility Features With Playwright
Discover how Playwright can help ensure accessibility in your applications. This article demonstrates how to use Playwright for testing keyboard navigation and identifying accessibility regressions before production....
Jan 31, 2024
Deploying Multiple Apps From a Monorepo to GitHub Pages
Explore deploying multiple front-end applications on GitHub Pages with our guide. Learn how to navigate the challenges of client-side routing and efficiently manage multiple apps in one repository....
Jan 24, 2024
I Broke My Hand So You Don't Have To (First-Hand Accessibility Insights)
A perspective of a temporarily impaired developer on accessibility both from the perspective of a user and of a software engineer. Using the web disabled “surprisingly” makes you think about people’s experience on the web. Let us take you on that journey!...
Dec 6, 2023
You Don't Need NgRx To Write a Good Angular App
NgRx is a great tool that allows you to manage state and side effects in Angular applications in a Redux-like manner. It streamlines state changes with its unidirectional data flow, and offers a structured approach to handling data and side effects. Numerous posts on our blog detail its strengths and affiliated techniques. Some Angular developers even argue that incorporating NgRx is imperative once an app expands beyond two features. While NgRx can undoubtedly enhance an Angular application or library by simplifying debugging, translating business logic into code, and improving the architecture, it does present a steep learning curve. Despite the provocative title, there is some truth to the statement: your app or library may indeed not need NgRx. Surprisingly, I successfully developed a suite of enterprise Angular libraries over five years without involving NgRx. In that project, we decided to opt out of using a state management library like NgRx because of its steep learning curve. Developers with varying levels of Angular expertise were involved, and the goal was to simplify their experience. My bold assertion is that, with careful consideration of architectural patterns, it is entirely possible to develop a robust app or library using only Angular, without any third-party libraries. Employing select design patterns and leveraging Angular's built-in tools can yield a highly maintainable app, even without a dedicated state management library. Having shared my somewhat audacious opinion, let me now support it by outlining a few patterns that facilitate the development of a maintainable, stateful Angular application or library without NgRx. Services and the Singleton Pattern Services provided in root or a module yield a shared instance across the entire app or module, effectively rendering them singletons. This characteristic makes them ideal for managing and sharing state across components without requiring a dedicated state management tool like NgRx. Particularly, for small to medium-sized applications, a "state service" can be a straightforward and effective alternative to a comprehensive state management solution when implemented correctly. To accurately implement state in a singleton service, consider the following: - Restrict state data to private properties and expose them only through public methods or observables to prevent external mutations. Such a pattern safeguards the integrity of your state by averting unauthorized modifications. - Utilize BehaviorSubjects or signals to enable components to respond to state changes. Both BehaviorSubject and SettableSignal retain the current value and emit it to new subscribers immediately. Components can then subscribe to these to receive the current value and any subsequent updates. - Expose public methods in your service that manage state modifications to centralize the logic for updating the state and incorporate validation, logging, or other necessary side effects. - When modifying state, always return a new instance of the data rather than altering the original data. This ensures that references are broken and components that rely on change detection can accurately detect changes. Good Component Architecture Distinguish your UI components into stateful (containers) and stateless (presentational) components. Stateful components manage data and logic, while stateless components merely receive data via inputs and emit events without maintaining an internal state. Do not get dragged into the rabbit hole of anti-patterns such as input drilling or event bubbling while trying to make as many components presentational as possible. Instead, use a Data Service Layer to provide a clean abstraction over backend API calls and handle error management, data transformation, caching, and even state management where it makes sense. Although injecting a service into a component technically categorizes it as a "smart" component, segregating the data access logic into a separate service layer ultimately enhances modularity, maintainability, scalability, and testability. Immutability A best practice is to always treat your state as immutable. Instead of modifying an object or an array directly, you should create a new copy with the changes. Adhering to immutability ensures predictability and can help in tracking changes. Applying the ChangeDetectionStrategy.OnPush strategy to components whenever possible is also a good idea as it not only optimizes performance since Angular only evaluates the component for changes when its inputs change or when a bound event is triggered, but it also enforces immutability. Change detection is only activated when a different object instance is passed to the input. Leveraging Angular Router Angular's router is a powerful tool for managing application state. It enables navigation between different parts of an application, allowing parameters to be passed along, effectively using the URL as a single source of truth for your application state, which makes the application more predictable, bookmarkable, and capable of maintaining state across reloads. Moreover, components can subscribe to URL changes and react accordingly. You can also employ router resolvers to fetch data before navigating to a route, ensuring that the necessary state is loaded before the route is activated. However, think carefully about what state you store in the URL; it should ideally only contain the state essential for navigating to a specific view of your application. More ephemeral states, like UI state, should be managed in components or services. Conclusion Angular provides lots of built-in tools and features you can effectively leverage to develop robust, maintainable applications without third-party state management libraries like NgRx. While NgRx is undoubtedly a valuable tool for managing state and side effects in large, complex applications, it may not be necessary for all projects. By employing thoughtful design patterns, such as the Singleton Pattern, adhering to principles of immutability, and leveraging Angular's built-in tools like the Router and Services, you can build a highly maintainable and stateful Angular application or library....
Sep 1, 2023
Content Projection in Front-end Frameworks Initially, I wanted to write a nice comprehensive guide on ng-content and content projection in Angular. But then I found out that my colleague Linda already wrote a wonderful article that covers the topic very well, so I thought about it a little and realized that pretty much every front-end framework has some implementation of the concept. So I decided to write a short article about content projection in general and show how it is implemented in some of the most popular front-end frameworks. What is Content Projection? Some of you may also remember it by the term "transclusion", a bit of a tongue-twister from the Angular.js days. Content projection is a way to take markup (HTML, components...) and slot it right into another component. It's like the Swiss Army knife in our coding toolbox, helping us create reusable components that can fit anywhere. Let's imagine a card component - something you see on pretty much every website these days. It can have a header, a body, maybe some image, and a footer. Now, what you put inside that card can change based on what you need. Maybe today it's a user profile, tomorrow it's a product description, and the day after, it's a blog post. The layout stays the same, but the content? That's entirely up to you, and that's the magic of content projection. Using this approach can help us reduce redundancy in our code and keep it DRY. It also ensures a more consistent user experience. It is, however, important to remember that content projection is not a silver bullet. It can be overused, and it can make your code harder to read and maintain. So, as with everything, use it wisely. How Does it Work Content projection is essentially about two main players: the receiving component (where the content is projected into) and the projecting component (which provides the content to be projected). The receiving component has specific placeholders, often denoted as slots or named slots in frameworks like Vue.js or Web Components. These slots serve as 'parking spaces' for the content from the projecting component. In general, we distinguish two types of content projection: single-slot and multi-slot. Single-slot content projection is when you have only one placeholder for the content. Multi-slot content projection is when you have multiple placeholders for the content. The placeholders can be named or unnamed. Named placeholders are used when you want to project different content into different placeholders. When you're defining the receiving component, you usually specify these slots without defining what will go into them. It's akin to laying out an empty stage for a play, with specific spots designated for props, but not specifying what those props will be. This gives you the flexibility to later decide and alter what content will 'perform' in those spaces. Now, when it comes to the projecting component, that's where the magic happens. Here, you'll take advantage of the slots that the receiving component provides, filling them with the specific content you want to project. For example, you might have a card component with a 'header' slot. When you use this card component elsewhere in your application, you can decide what gets projected into that 'header' slot. It could be a title for one instance of the card and an image for another. Now, let's see the concepts different frameworks use to allow content projection, starting with the Web Components way. The Web Components Way (Slots) As I mentioned before, a very popular way to implement content projection is with slots. The "slot" approach is not only used in Web Components, but also in Vue.js, Svelte, or Qwik. In Web Components, slots are defined using the tag. Let's illustrate how slots are used in Web Components with an example of a card component with three slots: header, body, and footer. The content that will be projected into these slots will be defined when the component is used. The component will look like this: ` This receiving component could be used in a projecting component like this: ` You can check out this example on StackBlitz. Vue.js As mentioned above, Vue.js and other frameworks also use the slot concept. Let's see how it's implemented in Vue.js. First, the receiving component: ` And then, the projecting component: ` Pretty much the only difference for you as a developer is that you use the v-slot directive instead of the slot attribute. Similarly, Qwik for example uses a q:slot attribute. You can check out the Vue.js example on StackBlitz too! The Angular Way (ng-content) In Angular, content projection is implemented using the tag. It is a tag that is used to mark the place where the content will be projected. It can be used in two ways: - As a tag with a select attribute, which is used to select the content that will be projected. The value of the select attribute is a CSS selector that is used to select the content that will be projected. If the select attribute is not present, all content will be projected. - As a tag without a select attribute, which will project all content. Let's have look at an example of a receiving card component in Angular: ` And then, the projecting component can be used like this: ` You can check out this example on StackBlitz. As you can see, the Angular way is not very different from the Web Components way. It uses the tag instead of the tag and a select directive instead of the name (or q-name) attribute, but the principles are very much the same. The React Way (children) So far, we covered the "standard" way of content projection. In React, however, it is not so straightforward as it does not have a concept of "slots" or "content projection". We can still achieve something similar using the children prop. It is a prop that is used to pass children to a component. It can be used in two ways: - As a prop with a function as a value, which is used to select the content that will be projected. The function will be called for each child and it should return a React element. If the function returns null or undefined, the child will not be rendered. This way allows us to have "multiple content projection". - As a prop without a function as a value, which will project all content. Let's see an example of a receiving card component in React: ` And then, we can project content like this: ` In this example, anything you pass as a header prop will be rendered in the header of the card component, and anything you pass as a footer prop will be rendered in the footer. Any children of the CardComponent will be rendered in the body. Here's the StackBlitz to play with! Conclusion As we've seen, content projection is a powerful and handy concept widely adopted by front-end frameworks to enable reusability, component composition, and dynamic content management. The implementation may vary between different libraries, such as Angular's , Vue's slots, or React's children prop. Nevertheless, the underlying principle remains the same: providing a way to inject custom content into predefined placeholders within a component, thereby extending its flexibility and reusability. We've taken a look at how this is achieved in Angular, Vue.js, Web Components, and even in React. But remember, just like with any tool or skill, it's important to think things through before you start implementing this in your app or library. It might be tempting to start throwing this into every component you build, but it's always worth taking a step back and considering whether it's the right tool for the job. Always aim for code that's not just flexible, but also easy to read, maintain, and efficient to run....
Aug 7, 2023
A Guide to (Typed) Reactive Forms in Angular - Part III (Creating Custom Form Controls)
Jun 26, 2023
A Guide to (Typed) Reactive Forms in Angular - Part II (Building Dynamic Superforms)
In the first blog post of the series, we learned about Angular reactive forms and the data structures behind them. When developing real-world applications, however, you often need to leverage dynamic forms, as writing boilerplate for every form and its specific cases can be tedious and time-consuming. In certain situations, it may even be necessary to retrieve information from an API to construct the forms. In this post, we will go over a convenient abstraction we can create to build dynamic and adaptable forms without repeating boilerplate. The trick is to create a "view model" for our data and use a service to transform that data into a reactive form. I was first introduced to this approach by my friend and former teammate Thomas Duft, and I've been using it ever since. The approach outlined in the linked article worked great with untyped forms, but since now we can get our forms strictly typed, we'll want to upgrade it. And here is where it gets a bit tricky. Remember how I mentioned you shouldn't predeclare your form groups earlier? If you want to recursively create a form from a config, you just have to. And it's a dynamic form, so you cannot easily type it. To solve the issue, I devised a trick inspired by a "Super Form" suggested by Bobby Galli. Assuming we will have interfaces defined for our data, using this approach, we can create dynamic type-safe forms. First, we'll create types for our form config: ` And then we'll create some type mappings: ` And now we can use our types in a service that will take care of creating nested dynamic forms: ` And that's it. Now we can use our FormService to create forms. Let's say we have the following User model: ` We can create a form for this user from config in the following way: ` If we would check the type of userForm.value now, we would see that it's correctly inferred as: ` Outputting the Dynamic Forms To display the dynamic forms, we can write a simple component that takes the FormSection or FormField as an Input() along with our FormGroup and displays the form recursively. We can use a setter to assign either field or section property when the view model is passed into the component, so we can conveniently use them in our template. Our form component's TypeScript code will look something like this: ` And our template will reference a new form component for each section field in case we have passed in a FormSection and it will have a switch case to display the correct control in case a FormField has been passed in: ` That way, we can display the whole form just by referencing one component, such as ` Check out an example on StackBlitz. In the next (and last) post of the series, we will learn about building custom Form Controls....
Jun 23, 2023
A Guide to (Typed) Reactive Forms in Angular - Part I (The Basics)
When building a simple form with Angular, such as a login form, you might choose a template-driven approach, which is defined through directives in the template and requires minimal boilerplate. A barebone login form using a template-driven approach could look like the following: ` ` However, when working on a user input-heavy application requiring complex validation, dynamic fields, or a variety of different forms, the template-driven approach may prove insufficient. This is where reactive forms come into play. Reactive forms employ a reactive approach, in which the form is defined using a set of form controls and form groups. Form data and validation logic are managed in the component class, which updates the view as the user interacts with the form fields. This approach requires more boilerplate but offers greater explicitness and flexibility. In this three-part blog series, we will dive into the reactive forms data structures, learn how to build dynamic super forms, and how to create custom form controls. In this first post, we will familiarize ourselves with the three data structures from the @angular/forms module: FormControl The FormControl class in Angular represents a single form control element, such as an input, select, or textarea. It is used to track the value, validation status, and user interactions of a single form control. To create an instance of a form control, the FormControl class has a constructor that takes an optional initial value, which sets the starting value of the form control. Additionally, the class has various methods and properties that allow you to interact with and control the form control, such as setting its value, disabling it, or subscribing to value changes. As of Angular version 14, the FormControl class has been updated to include support for typed reactive forms - a feature the Angular community has been wanting for a while. This means that it is now a generic class that allows you to specify the type of value that the form control will work with using the type parameter . By default, TValue is set to any, so if you don't specify a type, the form control will function as an untyped control. If you have ever updated your Angular project with ng cli to version 14 or above, you could have also seen an UntypedFormControl class. The reason for having a UntypedFormControl class is to support incremental migration to typed forms. It also allows you to enable types for your form controls after automatic migration. Here is an example of how you may initialize a FormControl in your component. ` Our form control, in this case, will work with string values and have a default value of "John Doe". If you want to see the full implementation of the FormControl class, you can check out the Angular docs! FormGroup A FormGroup is a class used to group several FormControl instances together. It allows you to create complex forms by organizing multiple form controls into a single object. The FormGroup class also provides a way to track the overall validation status of the group of form controls, as well as the value of the group as a whole. A FormGroup instance can be created by passing in a collection of FormControl instances as the group's controls. The group's controls can be accessed by their names, just like the controls in the group. As an example, we can rewrite the login form presented earlier to use reactive forms and group our two form controls together in a FormGroup instance: ` ` Notice we have to specify a formGroup and a formControlName to map the markup to our reactive form. You could also use a formControl directive instead of formControlName, and provide the FormControl instance directly. FormArray As the name suggests, similar to FormGroup, a FormArray is a class used to group form controls, but is used to group them in a collection rather than a group. In most cases, you will default to using a FormGroup but a FormArray may come in handy when you find yourself in a highly dynamic situation where you don't know the number of form controls and their names up front. One use case where it makes sense to resort to using FormArray is when you allow users to add to a list and define some values inside of that list. Let's take a TODO app as an example: ` In both of the examples provided, we instantiate FormGroup directly. However, some developers prefer to pre-declare the form group and assign it within the ngOnInit method. This is usually done as follows: ` If you try the above example in your IDE, you'll notice that the type of this.form.value is no longer inferred, and you won't get autocompletion for methods such as patchValue. This is because the FormGroup type defaults to FormGroup. To get the right types, you can either assign the form group directly or explicitly declare the generics like so: ` However, explicitly typing all your forms like this can be inconvenient and I would advise you to avoid pre-declaring your FormGroups if you can help it. In the next blog post, we will learn a way to construct dynamic super forms with minimal boilerplate....
Jun 20, 2023
Unit Testing Qwik Components
Qwik offers a unique approach to building fast, efficient web applications. However, we shouldn't forget about the quality of our code. In this blog post, we provide a comprehensive guide on how to set up and write tests for your Qwik components....
Feb 13, 2023