<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Poul H. Hansen</title>
    <description>The latest articles on Forem by Poul H. Hansen (@insanicae).</description>
    <link>https://forem.com/insanicae</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F143461%2Fd6ee4a42-23c0-4752-a08e-3530801f9050.jpg</url>
      <title>Forem: Poul H. Hansen</title>
      <link>https://forem.com/insanicae</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/insanicae"/>
    <language>en</language>
    <item>
      <title>You might not need shadow DOM</title>
      <dc:creator>Poul H. Hansen</dc:creator>
      <pubDate>Thu, 23 Apr 2020 18:51:29 +0000</pubDate>
      <link>https://forem.com/insanicae/you-might-not-need-shadow-dom-4nff</link>
      <guid>https://forem.com/insanicae/you-might-not-need-shadow-dom-4nff</guid>
      <description>&lt;p&gt;When talking about Web Components we often forget that it´s an umbrella term that covers a set of low level API's that work together to form the web´s native component model.&lt;/p&gt;

&lt;p&gt;It is a very common misconception that we need to use them &lt;strong&gt;all&lt;/strong&gt; in order to build Web Components.&lt;/p&gt;

&lt;p&gt;In fact, we really only need the custom element API in order to register our component name and class with the browser. However, combining custom elements with shadow DOM gives us out-of-the-box style isolation and DOM encapsulation, which is perfect for self-contained reusable components for our UIs.&lt;/p&gt;

&lt;p&gt;Creating a Web Component that does not use shadow DOM is perfectly fine, and in some cases, i´d advise against using shadow DOM at all.&lt;/p&gt;

&lt;p&gt;Let us go over some use-cases where I think shadow DOM might not be the right choice. But before that, a quick overview of what shadow DOM provides. &lt;/p&gt;

&lt;h2&gt;
  
  
  The short intro to shadow DOM
&lt;/h2&gt;

&lt;p&gt;Shadow DOM is all about &lt;strong&gt;encapsulation&lt;/strong&gt;. Due to the global nature of HTML, CSS and Javascript we´ve developed a lot of tools and methodologies to circumvent the issues over the years.&lt;/p&gt;

&lt;p&gt;Common issues include clashing element Id´s, classes or styles from the global stylesheet overriding 3rd party libraries and/or vice versa. Some of us still have to keep these things in mind when developing today depending on tooling.&lt;/p&gt;

&lt;p&gt;Shadow DOM fixes this by giving us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolated DOM tree:&lt;/strong&gt; The shadow DOM is self-contained and the outside cannot query elements on the inside (e.g. &lt;code&gt;document.querySelector&lt;/code&gt; wont return nodes from within the shadow tree)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped CSS:&lt;/strong&gt; Styles defined within the shadow DOM will not leak out, and outside styles will not bleed in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition:&lt;/strong&gt; Through the use of &lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; our elements can take outside nodes from the light DOM and place them in specific positions inside the shadow DOM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scoped CSS alone is incredibly powerful. Frameworks today all include some form of scoped styling that, during compile time, adds an attribute to the DOM element that is also added to the output CSS. This combination results in a very specific selector in your css (&lt;code&gt;a[data-v-fxfx-79]&lt;/code&gt;) that won´t bleed out and affect the outside DOM.&lt;/p&gt;

&lt;p&gt;However, this method &lt;strong&gt;does not prevent outside styles from leaking into your component&lt;/strong&gt;. This is where the true power of shadow DOM scoped styling really shines. Not only is it native to the browser, but it works both ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  So why not always use shadow DOM? 🤔
&lt;/h2&gt;

&lt;p&gt;We´ve just learned that the shadow DOM API gives us a set of incredibly powerful tools that enable us to build truly encapsulated reusable components. So why not use it everywhere?&lt;/p&gt;

&lt;p&gt;First of all, without a clear goal or use-case in our mind, we probably shouldn't just jump the gun and start enabling shadow DOM everywhere. As with every new technology, we should first do our research.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser support
&lt;/h3&gt;

&lt;p&gt;Whenever we look at cool new browser API´s we have to also take support into consideration. Luckily, shadow DOM is supported in all major browsers. However, some of us has to still support older browser like IE11 for a while still.&lt;/p&gt;

&lt;p&gt;We could polyfill for our IE11 users, right? 🤷‍♂️&lt;/p&gt;

&lt;p&gt;While polyfilling shadow DOM is possible, it is pretty hard, and the existing polyfills are invasive and slow.&lt;/p&gt;

&lt;p&gt;So instead of directly polyfilling the shadow DOM, compilers such as &lt;a href="https://stenciljs.com/"&gt;stencilJS&lt;/a&gt; fall back to scoped styles for IE11. While this does make our component usable, it also reintroduces the issue of scoped styling not preventing outside styles from bleeding in.&lt;/p&gt;

&lt;p&gt;This means that we have to cautiously test in IE11 that outside styles won´t affect the insides of our component. That sucks, as our component now behaves differently between browsers.&lt;/p&gt;

&lt;p&gt;So even though your components might be great candidates for shadow DOM, carefully weigh your options if you´re forced to support IE11. &lt;/p&gt;

&lt;h3&gt;
  
  
  Who are our consumers?
&lt;/h3&gt;

&lt;p&gt;The next thing I suggest looking into is, who are we making these components for? Is it our own internal product or are we making a component library to be consumed by the masses on &lt;strong&gt;npm&lt;/strong&gt; ?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A friend of mine said it quite well; A single component that needs to stand on its own with its own set of functionality is a good candidate for shadow DOM. While one or more components as part of an application might not need shadow DOM, as their intended use is much clearer and their markup less fragile.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The quote above got me thinking about the whole internal vs external thing. When introducing web components to an existing long running project, there is a good chance we already have some sort of design system in place already. Or at the very least, an extensive set of battle tested styles and markup.&lt;/p&gt;

&lt;p&gt;With this in mind, we should really think about what shadow DOM could solve for us that we haven´t already solved by using methodologies such as &lt;strong&gt;BEM&lt;/strong&gt; or &lt;strong&gt;ITCSS,&lt;/strong&gt; &lt;em&gt;or just a solid CSS structure&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Say we have the following classes in our design system stylesheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;    &lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;.card__header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;.card__body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;.card__footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now let us add  a new reusable component to the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fancy-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;FancyCardComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Host&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"card__header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s2"&gt;"header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"card__body"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"card__footer"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s2"&gt;"footer"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;small&gt;💡 I´m using &lt;a href="https://stenciljs.com"&gt;stencil&lt;/a&gt;, a web component compiler, in my example above&lt;/small&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At first glance we might expect our new &lt;code&gt;&amp;lt;fancy-card&amp;gt;&lt;/code&gt; component to &lt;strong&gt;just&lt;/strong&gt; &lt;strong&gt;work&lt;/strong&gt;. We´ve added the classes from our stylesheet, they worked before we added the component, so all is good, right?&lt;/p&gt;

&lt;p&gt;Not exactly...&lt;/p&gt;

&lt;p&gt;When we see the element in the browser, the only style applied will be from the &lt;code&gt;.card&lt;/code&gt; class on the &lt;code&gt;&amp;lt;fancy-card&amp;gt;&lt;/code&gt; element. This is because the element has a shadow root attached to the host element (&lt;code&gt;&amp;lt;fancy-card&amp;gt;&lt;/code&gt;), and as such, the divs within the component cannot be styled via CSS classes defined outside the component shadow root.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember how global styles won´t bleed into the shadow DOM? Well, this is how it works.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have no way of using our existing classes unless we refactor and include those styles inside the component shadow root. If the existing design system relies on &lt;strong&gt;sass variables&lt;/strong&gt;, we´d also need to import those in the component stylesheet.&lt;/p&gt;

&lt;p&gt;While refactoring itself is not a problem, as we do it all the time, the reason for which we are refactoring is. By moving the above HTML and CSS into the component, we haven´t solved anything that wasn´t already solved before.&lt;/p&gt;

&lt;p&gt;Now, I´m aware that the &lt;code&gt;&amp;lt;fancy-card&amp;gt;&lt;/code&gt; component might seem like a dumb example at first glance, but I´ve actually seen a lot of these components out there. In fact, I´ve done it myself when I first started looking into Web Components and thought I needed to convert everything.&lt;/p&gt;

&lt;p&gt;The solution to the above could instead be to turn off shadow DOM. The issue of the class styles not being applied inside the component would go away and we would still have a composable component ready to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;fancy-card&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Awesome product&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;lorem ipsum...&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"footer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Buy&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/fancy-card&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Stencil enables using &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; without shadow DOM enabled. In a vanilla web component the above markup would not work as natively, &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; is part of the shadow DOM API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some would probably argue that with the rather simple markup for the component and no complex functionality, it should not require javascript at all. Since it is merely a glorified div element. While I do agree that such a simple component should not require javascript, if it was to be part of a consumable component library, using it would be a lot easier than having to add the html structure plus the classes as a consumer. As long as we´re aware of the trade-offs!&lt;/p&gt;

&lt;h3&gt;
  
  
  A note on forms
&lt;/h3&gt;

&lt;p&gt;In a previous article, &lt;a href="https://dev.to/shadow-dom-and-forms/"&gt;Custom elements, shadow DOM and implicit form submission&lt;/a&gt;, I mentioned that we cannot query the shadow tree from the outside, elements such as &lt;code&gt;input&lt;/code&gt; or &lt;code&gt;textarea&lt;/code&gt; placed inside our shadow root will not work with an outside &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element. The inputs would simply be ignored as they are not in the same tree-order as the form.&lt;/p&gt;

&lt;p&gt;So if we wanted to create a &lt;em&gt;custom input&lt;/em&gt; component. We would have to either write custom functionality to circumvent this issue or...&lt;/p&gt;

&lt;p&gt;🥁🥁🥁&lt;/p&gt;

&lt;p&gt;Just not use shadow DOM 🤷‍♂️&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Ultimately, shadow DOM is not a requirement in order to build Web Components. However, the great synergy between shadow DOM, custom elements and CSS variables is worth exploring. There are already tons of great projects and stand-alone components out there that show the power and versatility of these API´s combined.&lt;/p&gt;

&lt;p&gt;I hope my post helped clear some of the confusion around shadow DOM and how it can help us tremendously when building Web Components.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>stenciljs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Using the url for state with Angular</title>
      <dc:creator>Poul H. Hansen</dc:creator>
      <pubDate>Sat, 29 Feb 2020 22:19:55 +0000</pubDate>
      <link>https://forem.com/insanicae/using-the-url-for-state-with-angular-1l1k</link>
      <guid>https://forem.com/insanicae/using-the-url-for-state-with-angular-1l1k</guid>
      <description>&lt;p&gt;A pretty common use-case for storing values in the &lt;code&gt;url&lt;/code&gt; in the form of &lt;strong&gt;query strings&lt;/strong&gt; are filters.&lt;br&gt;
Storing filters in the &lt;code&gt;url&lt;/code&gt; ensures that everyone with the link will see the same filterable content in their browsers.&lt;/p&gt;

&lt;p&gt;This is really great for ecommerce where people will often discuss with friends or ask for help before purchasing a product, and in doing so, might send a link of their filtered products along to their friends.&lt;/p&gt;

&lt;p&gt;While it may seem like a small thing, it´s actually a really great user experience to store product filters in the &lt;code&gt;url&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If users refresh their browser by hitting &lt;strong&gt;F5&lt;/strong&gt;, they will also return back to the same screen they were on before. Neat!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;But what about single page applications? 🤔&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Typically, in a SPA, routing happens on the client. Meaning our frameworks or libraries hook into the browser history, altering the state and updating the &lt;code&gt;url&lt;/code&gt; without a full page refresh.&lt;br&gt;
There is no roundtrip to the server and the whole page does not need to be rendered all over, which in itself, is &lt;strong&gt;also really nice&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;However, since the &lt;code&gt;url&lt;/code&gt; in this case is really only responsible of getting the application resources to the user upfront, it is not used for fetching any related data for the route.&lt;/p&gt;

&lt;p&gt;In Angular we normally use &lt;code&gt;services&lt;/code&gt; to fetch data. These services are then imported into our &lt;code&gt;components&lt;/code&gt; which use these &lt;code&gt;services&lt;/code&gt; to fetch data and render the view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;product-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;ul&amp;gt;
      &amp;lt;li *ngFor="let product of products"&amp;gt;{{ product.name }} {{ product.genre }} {{ product.platform }}&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
  `&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ProductListComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProductService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_products&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_products&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The example above is pretty simple. We inject the &lt;code&gt;ProductService&lt;/code&gt; and during the &lt;code&gt;ngOnInit&lt;/code&gt; lifecycle of the component, we subscribe to &lt;code&gt;getProducts()&lt;/code&gt; which returns&lt;br&gt;
a list of products when the api call is done. The response, a list of products in our case, is then set to the class field &lt;code&gt;products&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This field is then used in the &lt;code&gt;template&lt;/code&gt; to render the list of products in a normal unordered list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding filters&lt;/strong&gt; 💡&lt;/p&gt;

&lt;p&gt;Now lets say that we want to add two types of filters to our product list, genre and platform, to narrow down the list of games.&lt;/p&gt;

&lt;p&gt;Instead of storing the filter options on the component or in a service, let us use the &lt;code&gt;url&lt;/code&gt; instead and store our filters as query params.&lt;/p&gt;

&lt;p&gt;To access the query params we use the &lt;code&gt;ActivatedRoute&lt;/code&gt; service within our component.&lt;br&gt;
And to update the query params, we simply navigate 🤭 -- yeah, it is that simple!&lt;/p&gt;

&lt;p&gt;Getting the query params look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActivatedRoute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryParams&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/**
         * If the query-string is '?genre=rpg&amp;amp;platform=xbox'
         * the queryParams object will look like
         * { platform: 'xbox', genre: 'rpg }
         * */&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Depending on where you´re navigating from, setting query parameters can be done in a few different ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ genre: 'rpg' }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;RPG&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here we use the &lt;code&gt;routerLink&lt;/code&gt; directive on a &lt;code&gt;button&lt;/code&gt; to navigate to the same route as we´re on using &lt;code&gt;[routerLink]="[]"&lt;/code&gt; and set the query parameter &lt;code&gt;genre&lt;/code&gt; to &lt;code&gt;rpg&lt;/code&gt;, using the &lt;code&gt;queryParams&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;We use the &lt;code&gt;queryParamsHandling="merge"&lt;/code&gt; here to make sure we merge our queryParams with the already existing queryParams, overriding ones with identical keys. &lt;/p&gt;

&lt;p&gt;So clicking another &lt;code&gt;routerLink&lt;/code&gt; that has &lt;code&gt;[queryParams]="{ genre: 'racing' }"&lt;/code&gt; will directly override our current &lt;code&gt;genre&lt;/code&gt; query param, but a &lt;code&gt;routerLink&lt;/code&gt; with &lt;code&gt;[queryParams]="{ platform: 'xbox' }"&lt;/code&gt; would merge the two into &lt;code&gt;[queryParams]="{ genre: 'rpg', platform: 'xbox' }"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another option is doing it from within a component method using the &lt;code&gt;router&lt;/code&gt; service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;updateQueryParameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[],&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="na"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="na"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt; 
            &lt;span class="na"&gt;queryParamsHandling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;router.navigate&lt;/code&gt; function we navigate to the current route &lt;code&gt;[]&lt;/code&gt; and set the &lt;code&gt;queryParams&lt;/code&gt; object and the &lt;code&gt;queryParamsHandling&lt;/code&gt; option, just like we did with the directive.&lt;/p&gt;

&lt;p&gt;For the purpose of this example i´ll be using simple links to update the filters like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"product-filters"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;genres: &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ genre: 'rpg' }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;rpg&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ genre: 'platformer' }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;platformer&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ genre: 'racing' }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;racing&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ genre: null }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;clear&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"product-filters"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;platforms: &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ platform: 'playstation' }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;playstation&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ platform: 'switch' }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;switch&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ platform: 'xbox' }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;xbox&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"[]"&lt;/span&gt; &lt;span class="na"&gt;[queryParams]=&lt;/span&gt;&lt;span class="s"&gt;"{ platform: null }"&lt;/span&gt; &lt;span class="na"&gt;queryParamsHandling=&lt;/span&gt;&lt;span class="s"&gt;"merge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;clear&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;small&gt;&lt;em&gt;Above i´ve added an extra button to each list of filters to clear that particular filter from the url, by simply setting &lt;code&gt;null&lt;/code&gt;&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Now, clicking on the link for &lt;strong&gt;platformer&lt;/strong&gt; under genres, followed by the link to &lt;strong&gt;xbox&lt;/strong&gt; under platforms we end up with a url like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;localhost:4200/?genre=platformer&amp;amp;platform=xbox
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great, now we can set our filters in the &lt;code&gt;url&lt;/code&gt; and have our component listen to these changes using the &lt;code&gt;ActivatedRoute&lt;/code&gt; service. So far so good!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Putting it all together&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let us combine our &lt;code&gt;productService&lt;/code&gt; with our &lt;code&gt;queryParams&lt;/code&gt; observable stream from the &lt;code&gt;ActivatedRoute&lt;/code&gt; service to make sure the service is called each time our filters are updated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;product-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./product-list.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ProductListComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;products$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActivatedRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;productService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProductsService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;genre&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We´ve changed our class field &lt;code&gt;products: Product[]&lt;/code&gt; to &lt;code&gt;products$: Observable&amp;lt;Product[]&amp;gt;&lt;/code&gt; as it will now be an observable stream of data that gets updated when our filters update.&lt;/p&gt;

&lt;p&gt;We then listen to changes on the &lt;code&gt;queryParams&lt;/code&gt; stream from the &lt;code&gt;ActivatedRoute&lt;/code&gt; service and and use &lt;code&gt;switchMap&lt;/code&gt; to switch to the &lt;code&gt;productService.getProducts&lt;/code&gt; observable, passing in the filters we get from the &lt;code&gt;queryParams&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Since our queryParams could be non-existent we create an object and set default values to &lt;code&gt;""&lt;/code&gt; for both &lt;code&gt;genre&lt;/code&gt; and &lt;code&gt;platform&lt;/code&gt; to make sure we provide a sensible default.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The updated template for the &lt;code&gt;ProductListComponent&lt;/code&gt; now looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let product of products$ | async;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ product.name }} {{ product.genre }} {{ product.platform }}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, everytime we update a query parameter using one or more of our filters, the list of products will automatically get updated with new data from the service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That is pretty much all there is to it!&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;p&gt;An added benefit of using the &lt;code&gt;url&lt;/code&gt; for state such as this, is that the component that updates the filters does not need to be directly tied to the component showing the product list. &lt;br&gt;
They could live in completely separate parts of our application. &lt;/p&gt;

&lt;p&gt;For those using a state management library such as &lt;a href="https://ngrx.io"&gt;ngrx&lt;/a&gt;, this might seem familiar.&lt;br&gt;
Updating the query parameters here is much like dispatching an &lt;code&gt;action&lt;/code&gt;, and listening to changes in the queryParams state of the &lt;code&gt;url&lt;/code&gt; using the &lt;code&gt;router&lt;/code&gt;, is like listening to store changes with &lt;code&gt;selectors&lt;/code&gt; in ngrx.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Show me the code&lt;/strong&gt; 💻&lt;/p&gt;

&lt;p&gt;I have created an example repository for the &lt;code&gt;ProductListComponent&lt;/code&gt; example used in this article, you can find it here: &lt;a href="https://github.com/Phhansen/query-params-filter"&gt;query-params-filter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this has been useful to you in one way or another! 🙌&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also posted on my &lt;a href="https://www.hjorthhansen.dev/url-as-state-in-angular/"&gt;personal blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
