Skip to content

Developer Tools & Debugging in NgRx

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Developer Tools & Debugging in NgRx

When working on a complex software solution, we often find ourselves scratching our heads over a bug that was reported to us. It's essential to have proper tools to trace the issues which like to hide in our code execution paths. Luckily for the devs using NgRx in their project, the application state is kept in a single location and all the actions that can modify it are easily traceable with some great DevTools. As NgRx adheres to the redux pattern, we can use the same Redux DevTools as we would use for any other Redux base application. This tool is essential for me when debugging an NgRx based application.

If you haven't worked with NgRx yet, I recommend reading this post first where I introduced the NgRx pattern into a simple app.

Getting started

In order to make our NgRx store visible in the Redux DevTools, we need to pull in a module provided by NgRx platform - @ngrx/store-devtools. For the installation instructions, please visit the official installation guide.

After installing the Store Devtools using the AngularCLI schematics, the following code is automatically added to our AppModule:

@NgModule({
  /* other module properties */
  imports: [
    /* other imported modules */
    StoreDevtoolsModule.instrument({
      maxAge: 25, // Retains last 25 states
      logOnly: environment.production, // Restrict extension to log-only mode
    }),
  ],
})

maxAge property is limited to 25 by default for performance reasons - this is the limit of actions stored in the history tree.

logOnly is usually set to true on production build to limit the number of features used when connecting to Redux DevTools.

I suggest adding name property to our initial configuration to more easily find our state in the DevTools (as it will show all the other stores which might be used in other tabs open in the browser).

@NgModule({
  /* other module properties */
  imports: [
    /* other imported modules */
    StoreDevtoolsModule.instrument({
      name: 'DevTools & Debugging in NgRx'
      maxAge: 25, // Retains last 25 states
      logOnly: environment.production, // Restrict extension to log-only mode
    }),
  ],
})
Redux DevTools - state selector

With that minimal setup, we can already start using the Redux DevTools to start debugging our application.

You can access the Redux DevTools in the Redux tab on your browser developers tools. Chrome DevTools - Redux

Tracking actions

The first thing you can do in the Redux DevTools is track all the actions that have been dispatched within the application. Redux DevTools - list of action

For every selected action, you can see the current state value, what exactly has changed in the state as a result of this action, and the content of action object.

Redux DevTools - current state
Redux DevTools - state diff
Redux DevTools - action

Moreover, the extension gives you the possibility to "time travel" your application and skip some of the actions to see how it would affect the end result of the state.

You can either manually select the point in time to jump to or replay the whole sequence of action using timeline at the bottom.

Redux DevTools - jump to action and player
Redux DevTools - state replay

Those functionalities alone provide us with handful of possibilities on tracking how the state of our app is changing over time and pinpointing the possible issues.

Replicating the app behavior

Another very powerful feature of the Redux DevTools is tha posibility to dispatch the actions without the need of interacting with the UI. It's available as one of the tabs in the bottom extension's menu:

Redux DevTools - dispatcher

By using this feature, we can dispatch any action we want. This is extremely useful if we find the exact course of actions that is leading to an error, but it's hard or long to replicate using the UI. We can enter and dispatch the desired sequence of actions and get to the troublesome point in the app state with ease and in a reproducible manner.

Redux DevTools - dispatching actions

There are a few features that combine well with the aforementioned dispatching technique:

  • Persisting the state
  • Commiting and reverting the state
Redux DevTools - Persist and commit

When we select the persist option, the extension makes sure that our state is persisted and restored even after we reload the page. The commit option allows us to store the state at the specific point in time and treat it as a starting point (it's like saving the game before going on to fight with the boss 🤓).

You can perform as many actions as you want from this point on, but you'll always be able to restore the state to a point in time at which you've done a last commit. The restore functionality is only available in the log monitor and not the inspector.

Redux DevTools - Log monitor

This plays really well with dispatching actions directly from the extension. We can test and debug how our application behaves (ie. via Effects) when dipatching a specific action with always exactly the same comitted state. Also, it's easy to repeat by reverting the state and dispatching the action again.

NgRx Store Dev Tools options

So far we've covered many use-cases of the Redux DevTools, but we can configure it's behavior to our needs when setting up the StoreDevtoolsModule.

In real life applications, our action log might consist of hundreds of actions which might pollute our view of what is happening in the app. We can filter them out directly in the extension but that doesn't solve the issue of the limit on number of actions visible at once. We're still limited by whatever the limit we set, and for performance reasons, we should not take this limit off or set it too high. For debugging purposes, we might only be interested in certain type of actions or definitely know that some actions (ie. the one dispatched by Angular Router) might not be useful to us at the given moment. When setting up our StoreDevtoolsModule we're given 3 ways to filter the actions that will be sent to the Redux DevTools extension:

  • actionBlocklist
  • actionSafelist
  • predicate

The first two are the most common ones to use. We can either block specific patters of actions (which we know that are not of interest to us) or we can allow only certain types of actions. Both of them take an array of strings as a value and act as a regex on action type property to filter out only the ones we're interested in.

If we want to do more specific filtering, we can use predicate. It takes current state and action as parameters and is called for each dispatched action. To allow action to be passed to the Redux DevTools extension, it must return true.

With those techniques, we can narrow the scope of actions visible in the extension and therefore make it easier to get the grasp of what is happening in the app.

Conclusion

With the tools and techniques mentioned above, you should be able to debug your NgRx based application with a bit more ease. It's important to know the tools you have available so that you can use them when the need arises.

In case you have any questions you can always tweet or DM at me @ktrz. I'm always happy to help!

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

4 Angular Component Libraries That are Perfect for Beginners cover image

4 Angular Component Libraries That are Perfect for Beginners

For beginners starting with Angular, the journey can feel daunting, especially when it comes to setting up projects from scratch. However, there is a range of outstanding Angular component libraries that alleviate this initial hurdle: ready-made toolkits, empowering newcomers to start building Angular applications without the complexities of bootstrapping their projects. Let’s take a look! Angular Material Angular Material is a UI component library by Google for Angular applications. It provides a variety of pre-built components following the Material Design guidelines, offering customization options, responsive design, and accessibility features. It's seamlessly integrated with Angular and offers extensive documentation and community support. NG-ZORRO NG-ZORRO is an Angular UI component library based on Ant Design by Alibaba. It offers a variety of customizable components for building modern web applications. With seamless Angular integration, responsive design, and accessibility features, NG-ZORRO simplifies development while following Ant Design principles. NG Bootstrap NG Bootstrap is a library for Angular developers to easily incorporate Bootstrap components into their applications without relying on jQuery. It offers Angular-specific directives and components, ensuring compatibility and performance within Angular projects while maintaining Bootstrap's responsive design and customization options. Clarity Design System Clarity is an open-source design system by VMware that offers a set of Angular components for building enterprise-scale applications. It provides components specifically tailored for data-centric applications, dashboards, and complex user interfaces. Clarity Design System emphasizes clarity, consistency, and usability in its components. The beginner’s journey can be both exhilarating and challenging. However, with the advent of powerful component libraries like Angular Material, NG-ZORRO, NG Bootstrap, and the Clarity Design System, the path becomes significantly smoother. These libraries offer pre-built components and design systems that eliminate the need to bootstrap projects from scratch. As beginners embark on their Angular journey, these libraries provide a sturdy foundation, allowing them to dive into development with confidence. By harnessing the capabilities of these libraries, beginners can not only expedite their learning curve but also craft exceptional Angular applications with ease and efficiency....

Overview of the New Signal APIs in Angular cover image

Overview of the New Signal APIs in Angular

Overview of the New Signal APIs in Angular Google's Minko Gechev and Jeremy Elbourn announced many exciting things at NG Conf 2024. Among them is the addition of several new signal-based APIs. They are already in developer preview, so we can play around with them. Let's dig into it, starting with signal-based inputs and the new matching outputs API. Signal Based Inputs Discussions about signal-based inputs have been taking place in the Angular community for some time now, and they are finally here. Until now, you used the @Input() decorator to define inputs. This is what you'd have to write in your component to declare an optional and a required input: ` With the new signal-based inputs, you can write much less boilerplate code. Here is how you can define the same inputs using the new syntax: ` It's not only less boilerplate, but because the values are signals, you can also use them directly in computed signals and effects. That, effectively, means you get to avoid computing combined values in ngOnChanges or using setters for your inputs to be able to compute derived values. In addition, input signals are read-only. The New Output I intentionally avoid calling them signal-based outputs because they are not. They still work the same way as the old outputs. The Angular team has just introduced a new output API that is more consistent with the latest inputs API and allows you to write less boilerplate, similar to the new input API. Here is how you would define an output until now: ` Here is how you can define the same output using the new syntax: ` The thing I like about the new output API is that sometimes it happens to me that I forget to instantiate the EventEmitter because I do this instead: ` You won't forget to instantiate the output with the new syntax because the output function does it for you. Signal Queries I am sure most readers know the @ViewChild, @ViewChildren, @ContentChild, and @ContentChildren decorators very well and have experienced the pain of triggering the infamous ExpressionChangedAfterItHasBeenCheckedError or having the values unavailable when needed. Here is a refresher on how you would use these decorators until now: ` With the new signal queries, similar to the new input API, the values are signals, and you can use them directly in computed signals and effects. You can define the same queries using the new syntax: ` Jeremy Elbourn mentioned that the new signal queries have better type inference and are more consistent with the new input and output APIs. He also showcased a brand new feature not available with the old queries. You can now define a query as required, and the Angular compiler will throw an error if the query has no result, guaranteeing that the value won't be undefined. Here is how you can define a required query: ` Model Inputs Jeremy and Minko announced the last new feature is the model inputs. The name is vague, but the feature is cool—it simplifies the definition of two-way bindings. Until now, to achieve two-way binding, you would have to define an input and an output following a given naming convention. @Input and @Output had to be defined with the same name (followed by "Change" in the case of the output). Then, you could use the template's [()] syntax. ` ` That way, you could keep the value in sync between the parent and the child component. With the new model inputs, you can define a two-way binding with a single line of code. Here is how you can define the same two-way binding using the new syntax: ` The html template stays the same: ` The model function returns a writable signal that can be updated directly. The value will be propagated back to any two-way bindings. Conclusion The new signal-based APIs are a great addition to Angular. They allow you to write less boilerplate code and make your components more reactive. The new APIs are already in developer preview, so you can start playing around with them today. I look forward to seeing how the community will adopt these new features and what other exciting things the Angular team has in store for us, such as zoneless apps by default....

How to Write a Custom Structural Directive in Angular - Part 2 cover image

How to Write a Custom Structural Directive in Angular - Part 2

How to write a custom structural directive in Angular - part 2 In the previous article I've shown how you can implement a custom structural directive in Angular. We've covered a simple custom structural directive that implements interface similar to Angular's NgIf directive. If you don't know what structural directives are, or are interested in basic concepts behind writing custom one, please read the previous articlefirst. In this article, I will show how to create a more complex structural directive that: - passes properties into the rendered template - enables strict type checking for the template variables Starting point I am basing this article on the example implemented in the part 1 article. You can use example on Stackblitz as a starting point if you wish to follow along with the code examples. Custom NgForOf directive This time, I would like to use Angular's NgForOf directive as an example to re-implement as a custom CsdFor directive. Let's start off by using Angular CLI to create a new module, and directive files: ` First, we need to follow similar steps as with the CsdIf directive. - add constructor with TemplateRef, and ViewContainerRef injected - add an @Input property to hold the array of items that we want to display ` Then, in the ngOnInit hook we can render all the items using the provided template: ` Now, we can verify that it displays the items properly by adding the following template code to our AppComponent. ` It displays the items correctly, but doesn't allow for changing the displayed collection yet. To implement that, we can modify the csdForOf property to be a setter and rerender items then: ` Now, our custom directive will render the fresh items every time the collection changes (its reference). Accessing item property The above example works nice already, but it doesn't allow us to display the item's content yet. The following code will display "no content" for each template rendered. ` To resolve this, we need to provide a value of each item into a template that we are rendering. We can do this by providing second param to createEmbeddedView method of ViewContainerRef. ` The question is what key do we provide to assign it under item variable in the template. In our case, the item is a default param, and Angular uses a reserved $implicit key to pass that variable. With that knowledge, we can finish the renderItems method: ` Now, the content of the item is properly displayed: Adding more variables to the template's context Original NgForOf directives allows developers to access a set of useful properties on an item's template: - index - the index of the current item in the collection. - count - the length of collection - first - true when the item is the first item in the collection - last - true when the item is the last item in the collection - even - true when the item has an even index in the collection - odd - true when the item has an odd index in the collection We can pass those as well when creating a view for a given element along with the $implicit parameter: ` And now, we can use those properties in our template. ` Improve template type checking Lastly, as a developer using the directive it improves, the experience if I can have type checking in the template used by csdFor directive. This is very useful as it will make sure we don't mistype the property name as well as we only use the item, and additional properties properly. Angular's compiler allows us to define a static ngTemplateContextGuard methods on a directive that it will use to type-check the variables defined in the template. The method has a following shape: ` This makes sure that the properties of template rendered by our DirectiveClass will need to conform to DirectiveContext. In our case, this can be the following: ` Now, if we eg. try to access item's property that doesn't exist on the item's interface, we will get a compilation error: ` The same would happen if we made a typo in any of the context property names: ` Summary In this article, we've created a clone of Angular's built-in NgForOf directive. The same approach can be used to create any other custom directive that your project might need. As you can see, implementing a custom directive with additional template properties and great type checking experience is not very hard. If something was not clear, or you want to play with the example directive, please visit the example on Stackblitz. In case you have any questions, you can always tweet or DM me at @ktrz. I'm always happy to help!...

An example-based guide to CSS Cascade Layers cover image

An example-based guide to CSS Cascade Layers

CSS is actually good now! If you’ve been a web developer for a while, you’ll know this hasn’t always been the case. Over the past few years, a lot of really amazing features have been added that now support all the major browsers. Cascading and selector specificity have always been a pain point when writing stylesheets. CSS cascade layers is a new feature that provides us with a lot more power and flexibility for tackling this problem. We no longer need to resort to tricky specificity hacks or order-of-appearance magic. Cascade layers are really easy to get started with. I think the best way to understand how and when they are useful is by walking through some practical examples. In this post, we’ll cover: * What CSS cascade layers are and how they work * Real-world examples of using layers to manage style priorities * How Tailwind CSS leverages cascade layers What are CSS Cascade Layers? Imagine CSS cascade layers as drawers in a filing cabinet, each holding a set of styles. The drawer at the top represents the highest priority, so when you open the cabinet, you first access the styles in that drawer. If a style isn't found there, you move down to the next drawer until you find what you need. Traditionally, CSS styles cascade by specificity (i.e., more specific selectors win) and source order (styles declared later in the file override earlier ones). Cascade layers add a new, structured way to manage styles within a single origin—giving you control over which layer takes precedence without worrying about specificity. This is useful when you need to control the order of styles from different sources, like: * Resets (e.g., Normalize) * Third-party libraries (e.g., Tailwind CSS) * Themes and overrides You define cascade layers using the @layer rule, assigning styles to a specific layer. The order in which layers are defined determines their priority in the cascade. Styles in later layers override those in earlier layers, regardless of specificity or order within the file. Here’s a quick example: ` In this example, since the theme layer comes after base, it overrides the paragraph text color to dark blue—even though both declarations have the same specificity. How Do CSS Layers Work? Cascade layers allow you to assign rules to specific named layers, and then control the order of those layers. This means that: * Layers declared later take priority over earlier ones. * You don’t need to increase selector specificity to override styles from another layer—just place it in a higher-priority layer. * Styles outside of any layer will always take precedence over layered styles unless explicitly ordered. Let’s break it down with a more detailed example. ` In this example: * The unlayered audio rule takes precedence because it’s not part of the reset layer, even though the audio[controls] rule has higher specificity. * Without the cascade layers feature, specificity and order-of-appearance would normally decide the winner, but now, we have clear control by defining styles in or outside of a layer. Use Case: Overriding Styles with Layers Cascade layers become especially useful when working with frameworks and third-party libraries. Say you’re using a CSS framework that defines a keyframe animation, but you want to override it in your custom styles. Normally, you might have to rely on specificity or carefully place your custom rules at the end. With layers, this is simplified: ` There’s some new syntax in this example. Multiple layers can be defined at once. This declares up front the order of the layers. With the first line defined, we could even switch the order of the framework and custom layers to achieve the same result. Here, the custom layer comes after framework, so the translate animation takes precedence, no matter where these rules appear in the file. Cascade Layers in Tailwind CSS Tailwind CSS, a utility-first CSS framework, uses cascade layers starting with version 3. Tailwind organizes its layers in a way that gives you flexibility and control over third-party utilities, customizations, and overrides. In Tailwind, the framework styles are divided into distinct layers like base, components, and utilities. These layers can be reordered or combined with your custom layers. Here's an example: ` Tailwind assigns these layers in a way that utilities take precedence over components, and components override base styles. You can use Tailwind’s @layer directive to extend or override any of these layers with your custom rules. For example, if you want to add a custom button style that overrides Tailwind’s built-in btn component, you can do it like this: ` Practical Example: Layering Resets and Overrides Let’s say you’re building a design system with both Tailwind and your own custom styles. You want a reset layer, some basic framework styles, and custom overrides. ` In this setup: * The reset layer applies basic resets (like box-sizing). * The framework layer provides default styles for elements like paragraphs. * Your custom layer overrides the paragraph color to black. By controlling the layer order, you ensure that your custom styles override both the framework and reset layers, without messing with specificity. Conclusion CSS cascade layers are a powerful tool that helps you organize your styles in a way that’s scalable, easy to manage, and doesn’t rely on specificity hacks or the appearance order of rules. When used with frameworks like Tailwind CSS, you can create clean, structured styles that are easy to override and customize, giving you full control of your project’s styling hierarchy. It really shines for managing complex projects and integrating with third-party CSS libraries....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co