<?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>DEV Community: Taiga UI</title>
    <description>The latest articles on DEV Community by Taiga UI (@taiga-ui).</description>
    <link>https://dev.to/taiga-ui</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%2Forganization%2Fprofile_image%2F3604%2Fd234751d-c60a-45f0-8b10-0bd8172239f1.png</url>
      <title>DEV Community: Taiga UI</title>
      <link>https://dev.to/taiga-ui</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/taiga-ui"/>
    <language>en</language>
    <item>
      <title>Demystifying Taiga UI root component: portals pattern in Angular</title>
      <dc:creator>Alex Inkin</dc:creator>
      <pubDate>Tue, 26 Jan 2021 12:14:59 +0000</pubDate>
      <link>https://dev.to/taiga-ui/demystifying-taiga-ui-root-component-portals-pattern-in-angular-1fal</link>
      <guid>https://dev.to/taiga-ui/demystifying-taiga-ui-root-component-portals-pattern-in-angular-1fal</guid>
      <description>&lt;p&gt;Just before new year, &lt;a href="https://twitter.com/marsibarsi" rel="noopener noreferrer"&gt;Roman&lt;/a&gt;, my colleague, &lt;a href="https://indepth.dev/posts/1413/taiga-ui" rel="noopener noreferrer"&gt;announced&lt;/a&gt; our new Angular UI kit library &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui" rel="noopener noreferrer"&gt;Taiga UI&lt;/a&gt;. If you go through &lt;a href="https://taiga-ui.dev/getting-started" rel="noopener noreferrer"&gt;Getting started&lt;/a&gt; steps, you will see that you need to wrap your app with the &lt;code&gt;tui-root&lt;/code&gt; component. Let's see what it does and explore what portals are and how and why we use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a portal?
&lt;/h2&gt;

&lt;p&gt;Imagine you have a select component. It has a drop-down block with suggestions. If we keep it at the same position in DOM as the hosting component, we will run into all sorts of trouble. Items pop through and containers can chop off content:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FpDxYWjQAQw9IlXhKaiR9Z8KgaHuFouln4FJbb2W1AipjnJLIgxBBcs9__-nF8iAVD9A1ksueyCHi-bosc7bNO2EaxX_7BQUpPbZs16vemgdPIizFYAgvpUcPGeE5QYuK9Yz8gwaZ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FpDxYWjQAQw9IlXhKaiR9Z8KgaHuFouln4FJbb2W1AipjnJLIgxBBcs9__-nF8iAVD9A1ksueyCHi-bosc7bNO2EaxX_7BQUpPbZs16vemgdPIizFYAgvpUcPGeE5QYuK9Yz8gwaZ" width="774" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verticality issues are often solved with &lt;code&gt;z-index&lt;/code&gt;, effectively starting World War Z in your app. It's not uncommon to see values like 100, 10000, 10001. But even if you manage to get it right — &lt;code&gt;overflow: hidden&lt;/code&gt; would still get you there. So what can we do? Instead of having drop-down near its host we can show it in a dedicated container on top of everything. Then your application content can live in its own &lt;a href="https://developer.mozilla.org/ru/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context" rel="noopener noreferrer"&gt;isolated context&lt;/a&gt; eliminating &lt;code&gt;z-index&lt;/code&gt; problems. This container is exactly what a portal is. And it is what the Taiga UI root component sets up for you, among other things. Let's look at its template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;tui-scroll-controls&amp;gt;&amp;lt;/tui-scroll-controls&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tui-portal-host&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;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;ng-content&amp;gt;&amp;lt;/ng-content&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tui-dialog-host&amp;gt;&amp;lt;/tui-dialog-host&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ng-content&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;"tuiOverDialogs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tui-notifications-host&amp;gt;&amp;lt;/tui-notifications-host&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ng-content&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;"tuiOverNotifications"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tui-portal-host&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ng-content&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;"tuiOverPortals"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tui-hints-host&amp;gt;&amp;lt;/tui-hints-host&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ng-content&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;"tuiOverHints"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generic and dedicated portals
&lt;/h2&gt;

&lt;p&gt;Both &lt;code&gt;tui-dialog-host&lt;/code&gt; and &lt;code&gt;tui-portal-host&lt;/code&gt; are portals in their nature. But they work differently. Let's explore the second one first. Taiga UI uses it mostly to display drop-downs. But it's a generic container. It is controlled by a very simple service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="nf"&gt;@Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TuiPortalService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TuiPortalHostComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="n"&gt;componentFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ComponentFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="n"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Injector&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;ComponentRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;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="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addComponentChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;componentFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;({&lt;/span&gt;&lt;span class="n"&gt;hostView&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="n"&gt;ComponentRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;hostView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;addTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="n"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;EmbeddedViewRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;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="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTemplateChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;removeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EmbeddedViewRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&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;And the component itself is rather straightforward. All it does is show templates and dynamic components on top of everything. No other logic is included (except a little &lt;code&gt;position: fixed&lt;/code&gt; helper for iOS). It means that positioning, closing and the rest is handled by portal items on their own. It's a good idea to have a generic portal for special cases. Like a fixed «Scroll to top» button displayed above content or anything else you, as a library user might need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drop-downs
&lt;/h2&gt;

&lt;p&gt;If we were to architect a drop-down — we would need to come up with a positioning solution. We have several options here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Position drop-down once and prevent scrolling while it's open. This is what material does by default.&lt;/li&gt;
&lt;li&gt; Position once and close if scrolling occurred. That's how native drop-downs behave.&lt;/li&gt;
&lt;li&gt; Follow host position when it changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We went with the third option. It's not that trivial as it turned out. You cannot really get two positions in sync, even with &lt;code&gt;requestAnimationFrame&lt;/code&gt;. Because once you query the host position — it triggers a layout recalculation. So by the time the next frame comes and drop-down is positioned — the host already changes location a little bit. This causes visible jumps, even on fast machines. We got around that by using absolute positioning, rather than fixed. Because the portal container wraps the entire page, position values stay the same during scroll. If the host is in a fixed container, though, it would still jump. But we can detect that when we open the drop-down and use fixed positioning for it as well.&lt;/p&gt;

&lt;p&gt;And then there's this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2F6B9coKjAaDvDtM0HzbHiEuP9RXWe17C488dJN94Pfhe7ToKQ_-kj9M9xZYKPvO4tY5NJ7h9gLaQRJfC0AcJ_7SRLHriOsNM_jsBbn6bPgBRQDM-V6v_WLjkSDSEXm8lWJ96Xh4Gi" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2F6B9coKjAaDvDtM0HzbHiEuP9RXWe17C488dJN94Pfhe7ToKQ_-kj9M9xZYKPvO4tY5NJ7h9gLaQRJfC0AcJ_7SRLHriOsNM_jsBbn6bPgBRQDM-V6v_WLjkSDSEXm8lWJ96Xh4Gi" width="600" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the host leaves the visible area — we need to close the drop-down. That's a job for Obscured service. It detects when the host is fully obscured by anything and closes drop-down in that case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dialogs
&lt;/h2&gt;

&lt;p&gt;For dedicated portals study we can take a look at dialogs. Toast notifications and hints are very similar but there are some interesting topics to discuss with modals.&lt;/p&gt;

&lt;p&gt;This is how dialog host looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt;
   &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let item of dialogs$ | async"&lt;/span&gt;
   &lt;span class="na"&gt;polymorpheus-outlet&lt;/span&gt;
   &lt;span class="na"&gt;tuiFocusTrap&lt;/span&gt;
   &lt;span class="na"&gt;tuiOverscroll=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt;
   &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;
   &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;
   &lt;span class="na"&gt;aria-modal=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
   &lt;span class="na"&gt;[attr.aria-labelledby]=&lt;/span&gt;&lt;span class="s"&gt;"item.id"&lt;/span&gt;
   &lt;span class="na"&gt;[content]=&lt;/span&gt;&lt;span class="s"&gt;"item.component"&lt;/span&gt;
   &lt;span class="na"&gt;[context]=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;
   &lt;span class="err"&gt;[@&lt;/span&gt;&lt;span class="na"&gt;tuiParentAnimation&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/section&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;"overlay"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of being a generic host it has an &lt;code&gt;ngFor&lt;/code&gt; loop over particular items. This allows us to bundle some logic in, like focus trap and page scroll blocking. There is also a clever use of dependency injection here, allowing dialogs to be design and data model agnostic. Host collects observables with dialogs through a dedicated multi token, merges these streams and shows the result. That way you can have multiple designs for dialogs in the same app. Taiga UI has two built-in designs — base and mobile. But you can easily add your own. Let's see how.&lt;/p&gt;

&lt;p&gt;Dialog service returns &lt;code&gt;Observable&lt;/code&gt;. When you subscribe to it, modal popup is shown, when you terminate subscription it is closed. Dialog can also send back data through that stream. First, we design our dialog component. All that's important here, really, is that you can inject &lt;code&gt;POLYMORPHEUS_CONTEXT&lt;/code&gt; in constructor. It would contain an object with &lt;code&gt;content&lt;/code&gt; and &lt;code&gt;observer&lt;/code&gt; for a particular dialog instance. You can close dialog from within by calling &lt;code&gt;complete&lt;/code&gt; on &lt;code&gt;observer&lt;/code&gt; and you can send back data using &lt;code&gt;next&lt;/code&gt; method. Plus all the options you will provide to the service that we will create by extending an abstract class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;DIALOG&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PolymorpheusComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyDialogComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_OPTIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MyDialogOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;label&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="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sc"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;@Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyDialogService&lt;/span&gt; &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;AbstractTuiDialogService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyDialogOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DIALOG&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;defaultOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_OPTIONS&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;In it we provide default config and a component to use and we're all set.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Dialogs, like everything in Taiga UI use &lt;/em&gt;&lt;a href="https://github.com/TinkoffCreditSystems/ng-polymorpheus" rel="noopener noreferrer"&gt;&lt;em&gt;ng-polymorpheus&lt;/em&gt;&lt;/a&gt;&lt;em&gt; for customizable content. You can read more about making interface free, flexible components with it in &lt;/em&gt;&lt;a href="https://indepth.dev/posts/1314/agnostic-components-in-angular" rel="noopener noreferrer"&gt;&lt;em&gt;this article&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Focus trapping is handled by the &lt;code&gt;tuiFocusTrap&lt;/code&gt; directive. Since we have drop-downs later in DOM and we can have multiple dialogs open at the same time — we don't care if focus goes farther in the DOM. If it went somewhere prior to dialog though — we return focus back with a few helpers from &lt;code&gt;@taiga-ui/cdk&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="nf"&gt;@HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;focusin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;silent&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;'$&lt;/span&gt;&lt;span class="k"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;onFocusIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;containsOrAfter&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="n"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&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;}&lt;/span&gt;

  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;focusable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getClosestKeyboardFocusable&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="n"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;false&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="n"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&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;Blocking page scroll is dealt with by combination of a directive and some logic inside the root component. Root just hides scrollbars when a dialog is open, while Overscroll directive takes care of touch and wheel scroll. There's a &lt;a href="https://developer.mozilla.org/ru/docs/Web/CSS/overscroll-behavior" rel="noopener noreferrer"&gt;CSS rule for overscroll behavior&lt;/a&gt;. However it's not sufficient. It doesn't help when dialog is small enough that it doesn't have its own scroll. That's why we have a directive with some additional logic stopping scroll if it will happen in some patent node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: what else does tui-root do?
&lt;/h2&gt;

&lt;p&gt;As far as portals go — this covers most of it. Let's also take a quick look at what else is bundled with the root component. You saw in the template that it has &lt;code&gt;tui-scroll-controls&lt;/code&gt;. These are custom scrollbars that control global scroll. You may have also noticed named content projections like &lt;code&gt;&amp;lt;ng-content select="tuiOverDialogs"&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/code&gt;. With those you can slide some content in-between layers of Taiga UI if you need. For example, if you run another library for toasts or dialogs and want them properly placed vertically.&lt;/p&gt;

&lt;p&gt;It also registers several &lt;a href="https://github.com/TinkoffCreditSystems/ng-event-plugins" rel="noopener noreferrer"&gt;event manager plugins&lt;/a&gt; in the DI. You can read about them in a &lt;a href="https://indepth.dev/posts/1153/supercharge-event-management-in-your-angular-application" rel="noopener noreferrer"&gt;dedicated article&lt;/a&gt;. It is important that &lt;code&gt;TuiRootModule&lt;/code&gt; goes after &lt;code&gt;BrowserModule&lt;/code&gt; so they are registered at the right order. But don't worry — if you get it wrong you will see an assertion message in the console.&lt;/p&gt;

&lt;p&gt;That wraps it up for portals and the root component. Taiga UI is open-source and you can check it out on &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and npm. You can also browse the &lt;a href="https://taiga-ui.dev/" rel="noopener noreferrer"&gt;demo portal&lt;/a&gt; with documentation and play with it using this &lt;a href="https://stackblitz.com/edit/taiga" rel="noopener noreferrer"&gt;StackBlitz starter&lt;/a&gt;. Stay tuned for more articles on interesting features we have!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Taiga UI is a new Angular UI Kit that you should try</title>
      <dc:creator>Roman Sedov</dc:creator>
      <pubDate>Tue, 19 Jan 2021 08:06:23 +0000</pubDate>
      <link>https://dev.to/taiga-ui/taiga-ui-is-a-new-angular-ui-kit-that-you-should-try-4egg</link>
      <guid>https://dev.to/taiga-ui/taiga-ui-is-a-new-angular-ui-kit-that-you-should-try-4egg</guid>
      <description>&lt;p&gt;Hey, Angular devs!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/waterplea" rel="noopener noreferrer"&gt;@waterplea&lt;/a&gt; and I write articles about Angular on different blogs from time to time. We wrote almost all of them based on our experience of developing a big UI component library. We've been developing, refactoring and growing it for several years and testing our ideas on a big number of projects in our company.&lt;/p&gt;

&lt;p&gt;We are happy to announce that we published our library into open source!&lt;/p&gt;

&lt;p&gt;In this article I want to write about concepts and practices that we build our library with and tell you why you should try it for both new and old projects even with other components or UI libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Completely modular
&lt;/h2&gt;

&lt;p&gt;Let’s start with project organization. Taiga UI includes several layers of abstractions as separate packages.&lt;/p&gt;

&lt;h3&gt;
  
  
  @taiga-ui/cdk
&lt;/h3&gt;

&lt;p&gt;It is a fundamental package. There are many Angular directives, services, tokens, base classes and utils for more safe, abstract and easy work with Angular. This package can be used as an additional multi-tool for your Angular application of any complexity. It can be also a basis for creating your own UI Kit.&lt;/p&gt;

&lt;p&gt;Examples of entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/wiki/TuiDestroyService" rel="noopener noreferrer"&gt;TuiDestroyService&lt;/a&gt; to simplify “destroy$” subjects in components&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/wiki/Empty-instances" rel="noopener noreferrer"&gt;TuiFilterPipe&lt;/a&gt; and &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/wiki/TuiMapperPipe" rel="noopener noreferrer"&gt;TuiMapperPipe&lt;/a&gt; to handle values in a template without extra ChangeDetection cycles&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/wiki/Decorators" rel="noopener noreferrer"&gt;tuiPure&lt;/a&gt; decorator to memoize getters or class methods&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Our “cdk” is different from “@angular/cdk”. It is not a problem to use both of them, because it is not alternative, but addition&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  @taiga-ui/core
&lt;/h2&gt;

&lt;p&gt;This package includes basic components for building an interface and some tools to make apps easier. Here you can find things like root components, portals for dialogs and dropdowns, theming and animations. Core is a foundation for other packages with UI components. Design and common styles start here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq1jxb4lupbchiz269zss.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq1jxb4lupbchiz269zss.png" alt="Buttons"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  @taiga-ui/kit
&lt;/h2&gt;

&lt;p&gt;It is a large package that includes many components for building any interface. There are both simple components like avatar, tag or toggle and also composite as for example an input date component that is built with three basic components: textfield with a mask, dropdown and calendar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhb7ygnbsfhc14vf5gdlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhb7ygnbsfhc14vf5gdlt.png" alt="InputDateRange"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  @taiga-ui/addon-*
&lt;/h2&gt;

&lt;p&gt;Addons are several thematic packages that are built with cdk, core and kit. There are for example charts package, commerce package for working with currencies, money and card input. There is also a specific doc package that give tools to build your own demo portal as ours (the link will be below 🙂)&lt;/p&gt;

&lt;p&gt;We have the following structure with high-level packages built with base packages:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F72hpwhx64cf6so6kc8c6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F72hpwhx64cf6so6kc8c6.png" alt="cdk -&amp;gt; core -&amp;gt; kit -&amp;gt; addons"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here comes an important question&lt;/strong&gt;: why do I need to install several packages as dependencies if I only need a couple of components? What is their size?&lt;/p&gt;

&lt;p&gt;Well, we rewrote the structure of our libraries a few months ago. We moved all our packages to the Secondary Entry Points concept.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flt9ba2nrq3k04wok45yi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flt9ba2nrq3k04wok45yi.png" alt="SEPs for UI KIT"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But we also needed to keep the previous API of imports from the root of the package. That is why we built it in a unique way where every folder deeper is a Secondary Entry Point for the folder higher.&lt;/p&gt;

&lt;p&gt;Now all our packages give an opportunity to import entities both from Primary Entry Point and any level of depth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7cphgebqtdumopzdelc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7cphgebqtdumopzdelc2.png" alt="Taiga UI SEPS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By the way, the first option is sufficient because nesting can be solved by builder automatically. You do not need to think about depth of import.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This way of organization give us many benefits as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application bundle is smaller because all libraries become fully-treeshakable&lt;/li&gt;
&lt;li&gt;Any cyclic dependencies can be catched on library building stage&lt;/li&gt;
&lt;li&gt;The project is structurized better, there are no extra bindings between its entities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that &lt;strong&gt;you can import even just one entity from our library and be sure that there is no redundant code in your bundle&lt;/strong&gt;. There is no code duplication or excess dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizable
&lt;/h2&gt;

&lt;p&gt;All styles and colors of our library are built with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*" rel="noopener noreferrer"&gt;CSS custom properties&lt;/a&gt;. It allows making new custom themes easy and you can change them on the fly.&lt;/p&gt;

&lt;p&gt;Now we offer one main theme of Taiga UI but we have plans to create several simple and several unusual themes.&lt;/p&gt;

&lt;p&gt;If you want to customize a single component, there are also some methods to do that. In theory, you can redesign our Kit for your design system in a few hours and use it without worrying because we don’t make breaking changes in CSS-variables too to prevent layout bugs. For example, our company is already using a separate proprietary theme on top of the open source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agnostic
&lt;/h2&gt;

&lt;p&gt;We want to make our components so that every developer could adjust it for their specific case fast and easy.&lt;/p&gt;

&lt;p&gt;We do not try to envision every use case. Instead, we do not restrict appearance and use generics so components are not limited to a particular data model. You can read more about such concepts in the article by Alex Inkin &lt;a href="https://indepth.dev/posts/1314/agnostic-components-in-angular" rel="noopener noreferrer"&gt;“Agnostic components in Angular”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nevertheless, we take care of basic UX aspects to let you focus on your project features. For example, when the user focuses our textfield with a keyboard, it will show a hint after a second automatically to let the screen reader read it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fck9m0s3ltkbvjpe8akz3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fck9m0s3ltkbvjpe8akz3.gif" alt="Hint a11y"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Well engineered
&lt;/h2&gt;

&lt;p&gt;We respect the best developer practices and try to follow the Angular way in development of our libraries.&lt;/p&gt;

&lt;p&gt;We are not afraid of DI, all our components use OnPush and the whole project is developed with strict TypeScript mode (we are &lt;a href="https://medium.com/its-tinkoff/typescript-tricks-that-allow-you-to-scale-your-app-endlessly-95a0ff3d160d?source=friends_link&amp;amp;sk=80868c22b17ae2d335ead5a2c927fe18" rel="noopener noreferrer"&gt;very sensitive&lt;/a&gt; about typings). If you want to use SSR one day, our components will work correctly.&lt;/p&gt;

&lt;p&gt;You do not need to worry about unexpected values of wrong types from our components or utils. Our components even print asserts in dev mode if you pass incorrect data to them :)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read more Angular tips &amp;amp; tricks in my twitter: &lt;a class="mentioned-user" href="https://dev.to/marsibarsi"&gt;@marsibarsi&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  It’s big!
&lt;/h2&gt;

&lt;p&gt;We have 130+ components, 100+ directives, dozens of tokens, utils and tools.&lt;/p&gt;

&lt;p&gt;Right now you can build almost any idea of your interface quickly. And it isn't over yet: we have some ideas of next components and we are open to your requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to start using Taiga
&lt;/h2&gt;

&lt;p&gt;Visit our official website with documentation. Here you can see a demo of our components, learn about our libraries and find instructions how to add it into your project: &lt;a href="https://taiga-ui.dev/" rel="noopener noreferrer"&gt;taiga-ui.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to support us or see our progress, give a star and follow &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui" rel="noopener noreferrer"&gt;Taiga UI on Github&lt;/a&gt;. There you can ask any questions, propose an idea or contribute to our code.&lt;/p&gt;

&lt;p&gt;We also have &lt;a href="https://discord.com/channels/748677963142135818/757654627670818968" rel="noopener noreferrer"&gt;a channel in Angular discord&lt;/a&gt;. Feel free to contact us there!&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not goodbye
&lt;/h2&gt;

&lt;p&gt;We want to write more articles about how we organize and develop our libraries. Such articles can help you understand better how our UI Kit works. They will also explain all our tricks and practices of developing easy reusable components on Angular that are very helpful in application development too.&lt;/p&gt;

&lt;p&gt;Tell us your opinion about Taiga UI and share your ideas about which components, tools or processes you want to read first&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
