<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Alexander Poshtaruk on Medium]]></title>
        <description><![CDATA[Stories by Alexander Poshtaruk on Medium]]></description>
        <link>https://medium.com/@alexanderposhtaruk?source=rss-ae97ac398bf9------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*Jr0FPPwJYo2LNphW.</url>
            <title>Stories by Alexander Poshtaruk on Medium</title>
            <link>https://medium.com/@alexanderposhtaruk?source=rss-ae97ac398bf9------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 16 May 2026 17:20:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@alexanderposhtaruk/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Inject() over constructor DI cases in Angular]]></title>
            <link>https://medium.com/@alexanderposhtaruk/inject-over-constructor-di-cases-in-angular-13b1f9997e54?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/13b1f9997e54</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[inject]]></category>
            <category><![CDATA[dependency-injection]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Mon, 14 Apr 2025 13:54:19 GMT</pubDate>
            <atom:updated>2025-04-19T13:56:41.856Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*4p_HTcGVkd6awz0T.png" /></figure><p>This article is created by heavily using Claude.ai.</p><p>The article banner is a picture of <a href="https://www.freepik.com/author/graphue">Graphue</a></p><p>Angular’s Dependency Injection (DI) system is powerful, but knowing when to use constructor injection versus the newer inject() function can significantly improve your code quality. With the introduction of runInInjectionContext(), we now have even more control over dependency resolution.</p><p>In this article, we’ll explore five key scenarios where inject() (sometimes with runInInjectionContext) provides better solutions than traditional constructor injection.</p><h3>1. Standalone Functions (Route Guards, Interceptors)</h3><p><strong>Problem:</strong> Constructor injection requires class wrappers for simple functions.</p><pre>@Injectable({<br>  providedIn: &#39;root&#39;<br>})<br>export class AuthGuard implements CanActivate {<br><br>  constructor(<br>    private router: Router,<br>    private authService: AuthService<br>  ) {}<br><br>  canActivate(<br>    route: ActivatedRouteSnapshot,<br>    state: RouterStateSnapshot<br>  ): boolean {<br>    return this.authService.isLoggedIn() || this.router.navigate([&#39;/login&#39;]);<br>  }<br>}</pre><p><strong>Solution:</strong> inject() enables dependency access in functions.</p><pre>import { inject } from &#39;@angular/core&#39;;<br>import { Router } from &#39;@angular/router&#39;;<br><br>export function authGuard(): boolean {<br>  const router = inject(Router);<br>  const authService = inject(AuthService);<br><br>  return authService.isLoggedIn() || router.navigate([&#39;/login&#39;]);<br>}</pre><h3>Key Benefits:</h3><ul><li>less boilerplate code</li><li>No artificial class creation</li><li>Better tree-shakability</li><li>Now the <a href="https://angular.io/guide/routing-overview#standalone-guards">Angular-recommended approach</a>.</li></ul><p>As you may notice it is used with functional route guards.</p><h3>2. Factory Functions (Creating Customized Services)</h3><p>As for me — this case is similar to previous one but have different application point — creating configurable service instances in functional way.</p><pre>import { inject, InjectionToken } from &#39;@angular/core&#39;;<br><br>// Token for different logger instances<br>export const USER_LOGGER = new InjectionToken&lt;Logger&gt;(&#39;user.logger&#39;);<br>export const SYSTEM_LOGGER = new InjectionToken&lt;Logger&gt;(&#39;system.logger&#39;);<br><br>// A factory that creates specialized logger instances<br>export function createLogger(category: string, minLevel: &#39;debug&#39;|&#39;info&#39;|&#39;error&#39;) {<br>  const configService = inject(ConfigService);<br><br>  return {<br>    debug: (msg: string) =&gt; {<br>      if (minLevel === &#39;debug&#39; &amp;&amp; configService.isDebugEnabled) {<br>        console.log(`[${category}][DEBUG] ${msg}`);<br>      }<br>    },<br>    info: (msg: string) =&gt; {<br>      if (minLevel === &#39;debug&#39; || minLevel === &#39;info&#39;) {<br>        console.log(`[${category}][INFO] ${msg}`);<br>      }<br>    },<br>    error: (msg: string) =&gt; {<br>      console.error(`[${category}][ERROR] ${msg}`);<br>    }<br>  };<br>}<br><br>// Creating provideFunction<br>export function provideUserLogger(minLevel: &#39;debug&#39;|&#39;info&#39;|&#39;error&#39;) {<br>  return {<br>    provide: USER_LOGGER,<br>    useFactory: () =&gt; createLogger(&#39;USER&#39;, minLevel)<br>  }<br>}<br><br>export function provideSystemLogger(minLevel: &#39;debug&#39;|&#39;info&#39;|&#39;error&#39;) {<br>  return {<br>    provide: SYSTEM_LOGGER,<br>    useFactory: () =&gt; createLogger(&#39;SYSTEM&#39;, minLevel)<br>  }<br>}<br><br>// Register in config file which will be used for Angular pp bootstrapping<br>export const appConfig: ApplicationConfig = {<br>  providers: [<br>    // Other providers...<br>    ConfigService,<br>    provideUserLogger(&#39;info&#39;),<br>    provideSystemLogger(&#39;error&#39;)<br>  ]<br>};</pre><p>Well, remembering to provide ConfigService to be able to use SYSTEM_LOGGER and USER_LOGGER providers is not very convenient, so better to modify provideSystemLogger and provideUserLogger to do that for us:</p><pre>export function provideUserLogger(minLevel: &#39;debug&#39;|&#39;info&#39;|&#39;error&#39;) {<br>  return [<br>    ConfigService,<br>    {<br>    provide: USER_LOGGER,<br>    useFactory: () =&gt; createLogger(&#39;USER&#39;, minLevel),<br>  }]<br>}<br><br>export function provideSystemLogger(minLevel: &#39;debug&#39;|&#39;info&#39;|&#39;error&#39;) {<br>  return [<br>    ConfigService,<br>    {<br>    provide: SYSTEM_LOGGER,<br>    useFactory: () =&gt; createLogger(&#39;SYSTEM&#39;, minLevel),<br>  }]<br>}</pre><p>Here is <a href="https://stackblitz.com/edit/stackblitz-starters-kphebv3x?file=src%2Fmain.ts">a playground</a>.</p><h3>3. Lazy Injection (On-Demand Service Loading)</h3><p>Constructor injection instantiates all services upfront, even if they’re rarely used. inject() allows delayed injection.</p><p>It work by using the runInInjectionContext function. This function lets you create an injection context at runtime, allowing inject() to be called outside the component initialization.</p><pre>import { Component, inject, Injector, runInInjectionContext } from &#39;@angular/core&#39;;<br>import { HeavyDataService } from &#39;./services&#39;;<br><br>@Component({<br>  selector: &#39;app-heavy&#39;,<br>  template: &#39;...&#39;<br>})<br>export class HeavyComponent {<br>  // Store the injector itself<br>  private injector = inject(Injector);<br><br>  // Service is not injected at initialization<br><br>  private loadHeavyData() {<br>    // Create injection context when needed<br>    return runInInjectionContext(this.injector, () =&gt; {<br>      // Now we can safely use inject()<br>      const heavyService = inject(HeavyDataService);<br>      return heavyService.fetchData();<br>    });<br>  }<br><br>  onUserAction() {<br>    // Heavy service only injected when this is called<br>    const data = this.loadHeavyData();<br>    // Process data...<br>  }<br>}</pre><p>Looks good, doesn’t it? Until my friend told me that to make HeavyDataService really lazy we need to use <em>import</em> O_o:</p><pre>  private loadHeavyData() {<br>    // Create injection context when needed<br>    return runInInjectionContext(this.injector, () =&gt; {<br>      // Now we can safely use inject()<br>      return import(&#39;./services&#39;).then(({ HeavyDataService }) =&gt; {<br>      const heavyService = inject(HeavyDataService);<br>      return heavyService.fetchData();<br>    });<br>  }</pre><h3>4. Multi-Level Inheritance (Avoiding Constructor Hell)</h3><p>TBH I am not a fan of inheriting component classes in Angular (composition over inheritance). I think it brings more mess than DRY benefits. But if you like it:</p><pre>@Component({...})<br>export class BaseComponent {<br>  protected readonly router = inject(Router);<br>}<br><br>@Component({...})<br>export class MiddleComponent extends BaseComponent {<br>  protected readonly userService = inject(UserService);<br>}<br><br>@Component({...})<br>export class ChildComponent extends MiddleComponent {<br>  // Has access to both `router` and `userService`<br>  // No complex constructor chaining needed!<br>}</pre><h3>Benefits:</h3><ul><li>No super() boilerplate.</li><li>Cleaner, more maintainable inheritance.</li><li>A child class can override a parent’s injected service by using inject() with the same token, creating its own instance.</li></ul><h3>5. Dynamic Providers (Runtime Dependency Switching)</h3><p>Constructor injection requires static dependencies. inject() enables dynamic service selection.<br> This case is very close to example #3 with Lazy service instantiation but brings more conditional logic to the previous example.</p><pre>import { Component, inject, Injector, runInInjectionContext } from &#39;@angular/core&#39;;<br>import { FeatureFlagService, NewImplementationService, LegacyImplementationService } from &#39;./services&#39;;<br><br>@Component({<br>  selector: &#39;app-dynamic&#39;,<br>  template: &#39;...&#39;<br>})<br>export class DynamicComponent {<br>  private injector = inject(Injector);<br>  private featureFlag = inject(FeatureFlagService);<br><br>  // Don&#39;t inject services yet<br>  private service: any;<br><br>  constructor() {<br>    // Get the right service based on feature flag<br>    this.service = this.getService();<br>  }<br><br>  private getService() {<br>    return runInInjectionContext(this.injector, () =&gt; {<br>      // Now we can conditionally inject<br>      if (this.featureFlag.isNewFeatureEnabled) {<br>        return inject(NewImplementationService);<br>      } else {<br>        return inject(LegacyImplementationService);<br>      }<br>    });<br>  }<br><br>  doSomething() {<br>    this.service.method();<br>  }<br>}</pre><h3>6. Type Inference with inject() vs Constructor DI</h3><p>When using InjectionTokens with constructor DI, you need to manually specify the type. Even more: you can get the type annotation wrong. Angular/TypeScript doesn’t check whether the type annotation matches the type of the InjectionToken/class.</p><pre>// Define a token with a type<br>const CONFIG = new InjectionToken&lt;AppConfig&gt;(&#39;app.config&#39;);<br><br>@Component({...})<br>class MyComponent {<br>  constructor(<br>    // Must manually specify the type here<br>    @Inject(CONFIG) private config: AppConfig<br>  ) {}<br>}</pre><p>Moreover, Constructor parameter decorators are not part of the ECMAScript Decorators standard. They will be unsupported once the experimentalDecorators option is removed from the TypeScript compiler</p><p>The inject() function automatically infers the correct type from the token:</p><pre>// Define a token with a type<br>const CONFIG = new InjectionToken&lt;AppConfig&gt;(&#39;app.config&#39;);<br><br>@Component({...})<br>class MyComponent {<br>  // Type is automatically inferred as AppConfig<br>  private config = inject(CONFIG);<br><br>  ngOnInit() {<br>    // TypeScript knows this is AppConfig<br>    console.log(this.config.apiUrl);<br>  }<br>}</pre><h3>7. Changes in ES2022 and future deprecation of ‘useDefineForClassFields’ compiler option in typescript.</h3><p>This info was introduced by Jeremy Elbourn (team lead of Angular team) in <a href="https://github.com/angular/angular/discussions/59522#discussioncomment-12781971">github issue comment</a>:</p><blockquote><em>Additionally, we’re adding one new recommendation: Prefer the inject function over constructor parameter injection</em></blockquote><blockquote><em>We’re adding this new recommendation in light of the introduction of class fields in ECMAScript 2022.</em></blockquote><p><em>Next text is copied from Jeremy Elbourn’s github comment:</em></p><p>Here is a basic example:</p><pre>@Component({ /* ... */ })<br>export class UserProfile {<br>  private user = this.userData.getCurrent();<br><br>  constructor(private userData: UserData) { }<br>}</pre><p>This example works just fine when TypeScript emits ECMAScript versions less than ES2022. In these versions, the compiled JavaScript looks like this:</p><pre>// Emitting ES2017<br>export class UserProfile {<br>    constructor(userData) {<br>        // The field initializer is inlined into the constructor<br>        this.userData = userData;<br>        this.user = this.userData.getCurrent();<br>    }<br>}</pre><p>However, in ES2022 with the <a href="https://www.typescriptlang.org/tsconfig/#useDefineForClassFields">useDefineForClassFields</a> option, the output looks like this:</p><pre>// Emitting ES2022<br>export class UserProfile {<br>    userData;<br>    user = this.userData.getCurrent(); // Error! userData is not yet initialized!<br>    constructor(userData) {<br>        this.userData = userData;<br>    }<br>}</pre><p>This output throws an error because the field initializer runs before the constructor and tries to use the injected dependency before it’s available. To work around this with constructor injection, you would write your code like this:</p><pre>@Component({ /* ... */ })<br>export class UserProfile {<br>  // Field declaration is separated from initialization.<br>  private user: User;<br><br>  constructor(private userData: UserData) {<br>    this.user = userData.getCurrent();<br>  }<br>}</pre><p>Many developers find the separation of field declaration and initialization to be undesirable. Fortunately, the inject function neatly sidesteps this problem:</p><pre>@Component({ /* ... */ })<br>export class UserProfile {<br>  private userData = inject(UserData);<br>  private user = userData.getCurrent();<br>}</pre><h3>Appendix: Situations Where inject() Won’t Work in Angular</h3><h3>1. Outside Injection Context (more <a href="https://angular.dev/guide/di/dependency-injection-context">here</a>)</h3><p>Here are particular cases:</p><h3>a) In Asynchronous Code</h3><pre>@Component({...})<br>class MyComponent {<br>  constructor() {<br>    // This will fail<br>    setTimeout(() =&gt; {<br>      const service = inject(MyService); // ERROR<br>    }, 1000);<br>  }<br>}</pre><h3>b) In Event Handlers</h3><pre>@Component({<br>  template: &#39;&lt;button (click)=&quot;handleClick()&quot;&gt;Click&lt;/button&gt;&#39;<br>})<br>class MyComponent {<br>  handleClick() {<br>    // ERROR: No injection context in event handler<br>    const service = inject(MyService);<br>  }<br>}</pre><h3>c) In Subscription Callbacks</h3><pre>@Component({...})<br>class MyComponent {<br>  constructor() {<br>    const observable = inject(DataService).getData();<br><br>    observable.subscribe(data =&gt; {<br>      // ERROR: No injection context in subscription callback<br>      const logger = inject(LoggerService);<br>      logger.log(data);<br>    });<br>  }<br>}</pre><h3>d) In Standalone Functions Without runInInjectionContext</h3><pre>// Standalone utility function<br>export function formatData(data: any) {<br>  // ERROR: No injection context<br>  const formatter = inject(FormatterService);<br>  return formatter.format(data);<br>}</pre><h3>e) inject(…) won’t work in ngOnInit method without runInInjectionContext.</h3><pre>  // doesn&#39;t work<br>  ngOnInit(): void {<br>    // We need to use runInInjectionContext here<br>    this.serviceB = inject(MyService);<br>  }<br><br>// works  <br>  ngOnInit(): void {<br>    runInInjectionContext(this.injector, () =&gt; {<br>      this.serviceB = inject(MyService);<br>    });<br>  }</pre><p>Actually, all the cases above are solved by wrapping in <em>runInInjectionContext</em> helper function.</p><h3>2. In Regular (Non-Angular) Classes</h3><pre>// Regular class, not managed by Angular DI<br>class RegularClass {<br>  constructor() {<br>    // ERROR: No injection context<br>    const service = inject(SomeService);<br>  }<br>}</pre><h3>Final Verdict</h3><ol><li>Use constructor injection for standard component/service DI but consider moving to inject() if you plan to migrate to es2022.</li><li>Use inject() for edge cases like functions, factories, lazy loading, inheritance, and dynamic providers</li></ol><h3>More to read:</h3><ol><li><a href="https://riegler.fr/blog/2025-01-08-inject-not-service-locator">“The inject function is not a service locator”</a> by Matthieu Riegler</li><li><a href="https://x.com/El_Extremal/status/1730944883660136528">Interesting drawback of using inject function</a> in Angular 16+ (I did not check it on newer versions). Case: Angular 16+ pipe used with the template child component input value. Drawback: if in pipe we inject ChangeDetectorRef with ‘inject’ function: cdRef will be from ChildComponent; but if we inject ChangeDetectorRef using constructor injection, it will be from the ParentComponent.</li></ol><p>Like this article? Follow me on <a href="https://twitter.com/El_Extremal">Twitter</a> or <a href="https://buymeacoffee.com/nqypxi6"><strong>Buy me a coffee</strong></a>!</p><p><em>Originally published at </em><a href="https://dev.to/oleksandr/inject-over-constructor-di-cases-in-angular-1m1h"><em>https://dev.to</em></a><em> on April 14, 2025.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=13b1f9997e54" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Angular CLI flows. Big picture.]]></title>
            <link>https://medium.com/angular-in-depth/angular-cli-flows-big-picture-9ed1a0d1930?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/9ed1a0d1930</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[angular-cli]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Fri, 01 May 2020 06:01:11 GMT</pubDate>
            <atom:updated>2022-03-23T11:03:29.608Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FullgTvyBUnrCp8KSqvrRw.png" /><figcaption>Photo by <a href="https://unsplash.com/@davidpisnoy?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">David Pisnoy</a> on <a href="https://unsplash.com/s/photos/tools?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p><em>Builders, custom typescript transformers, custom tslint rules, schematics — how not to be overwhelmed and lay it all out in your head?</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/743/0*W9HYGsVPU73ZmZy5.png" /></figure><blockquote><a href="https://medium.com/angular-in-depth"><strong><em>AngularInDepth</em></strong></a><strong><em> is moving away from Medium. </em></strong><a href="https://indepth.dev/angular-cli-flows-big-picture/"><strong><em>This article</em></strong></a><strong><em>, its updates and more recent articles are hosted on the new platform </em></strong><a href="https://indepth.dev/"><strong><em>inDepth.dev</em></strong></a></blockquote><p>I don&#39;t know how about you but I got confused with a variety of tools that Angular CLI provides for some not straightforward Angular environment tasks. Builders, schematics, typescript transformers, custom <em>tslint</em> rules, AST — what are these all about, why do we need them and when do we have to use them? My brain was bleeding…</p><p>At last, I found a time to dig deeper and sort information about these tools (Hooray!)</p><p>Let&#39;s review them one by one.</p><p>(Angular CLI 9.x codebase is used).</p><h3>Builders</h3><p>What are builders in Angular?<br>In Angular builders are used to do some routine tasks: build code, run lint, run unit tests, deploy code to host-provider.</p><blockquote>A number of Angular CLI commands run a complex process on your code, such as linting, building, or testing. The commands use …<em>builders</em>, which apply another tools to accomplish the desired task.</blockquote><blockquote>Angular provides … builders that are used by the CLI for commands such as ng build, ng test, and ng lint. Default target configurations … can be found (and customized) in the &quot;architect&quot; section of the <a href="https://angular.io/guide/workspace-config">workspace configuration file</a>, angular.json.</blockquote><blockquote>You can also extend and customize Angular by creating your own builders, which you can run using the <a href="https://angular.io/cli/run">ng run CLI command</a>.</blockquote><blockquote><a href="https://angular.io/guide/cli-builder"><em>https://angular.io/guide/cli-builder</em></a></blockquote><p>Let’s start with understanding what builders are used for and then explore how they are implemented.</p><p>If you run <strong><em>ng build </em></strong>command — Angular CLI actually runs the builder handler function (<strong><em>build</em></strong> in our case). Let’s go step by step and see what actually goes on behind the scenes.</p><p>*Don&#39;t forget that your <a href="https://indepth.dev/making-an-angular-project-mono-repo-with-ngrx-state-management-and-lazy-loading/">monorepo project</a> can have a few applications, and in angular.json you specify builder for each specific project. And to start builder for a concrete project with Angular CLI you should add project name to the command, for example: ng build app1 (you can read more in my monorepo article <a href="https://indepth.dev/making-an-angular-project-mono-repo-with-ngrx-state-management-and-lazy-loading/">here</a>)</p><ol><li>Read config in angular.json and find respective builder (projects-&gt;<em>projectName</em>-&gt;architect-&gt;<strong>build-&gt;</strong>builder)</li></ol><pre>&quot;builder&quot;: &quot;@angular-devkit/build-angular:browser&quot;, // original</pre><pre>OR</pre><pre>&quot;builder&quot;: &quot;@angular-builders/custom-webpack:browser&quot;, // custom</pre><p>Here is <a href="https://github.com/angular/angular-cli/blob/9de389fb770568919d6f2465cc155e0835926eaf/packages/angular_devkit/build_angular/src/browser/index.ts">code of <em>build-angular:browser</em></a> builder.</p><p>2. Create a builder instance and run it</p><pre>export default createBuilder&lt;json.JsonObject &amp; BrowserBuilderSchema&gt;(buildWebpackBrowser);</pre><p>3. The builder runs its standard tasks:</p><p>a) <a href="https://github.com/angular/angular-cli/blob/9de389fb770568919d6f2465cc155e0835926eaf/packages/angular_devkit/build_angular/src/browser/index.ts#L239">assertCompatibleAngularVersion</a></p><p>b) <a href="https://github.com/angular/angular-cli/blob/9de389fb770568919d6f2465cc155e0835926eaf/packages/angular_devkit/build_angular/src/browser/index.ts#L209">buildBrowserWebpackConfigFromContext</a> and <a href="https://github.com/angular/angular-cli/blob/9de389fb770568919d6f2465cc155e0835926eaf/packages/angular_devkit/build_angular/src/browser/index.ts#L260">runWebpack</a> (webpack starts typescript compiler for your code)</p><p>c) <a href="https://github.com/angular/angular-cli/blob/9de389fb770568919d6f2465cc155e0835926eaf/packages/angular_devkit/build_angular/src/browser/index.ts#L579">copyAssets</a></p><p>d) <a href="https://github.com/angular/angular-cli/blob/9de389fb770568919d6f2465cc155e0835926eaf/packages/angular_devkit/build_angular/src/browser/index.ts#L603">generateBundleStats</a></p><p>e) <a href="https://github.com/angular/angular-cli/blob/9de389fb770568919d6f2465cc155e0835926eaf/packages/angular_devkit/build_angular/src/browser/index.ts#L712">generate index.html</a></p><p>f) <a href="https://github.com/angular/angular-cli/blob/9de389fb770568919d6f2465cc155e0835926eaf/packages/angular_devkit/build_angular/src/browser/index.ts#L741">Apply service worker</a> if needed</p><p>and we get bundle files (index.html, css, js files in ./dist folder)</p><h4>So what are builders used for?</h4><p>Actually, they can be used for everything about your codebase: build, dev-server, run unit tests, run linter, etc</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/834/1*BGbM18dNoX31ol0pg525Hg.png" /></figure><p>Now you can assume what <strong><em>ng add</em></strong> command does — among many other things it adds new records to angular.json file (adding a new builder) — we will talk about <em>ng add</em> a bit later.</p><p>Let&#39;s run <strong><em>ng add </em></strong><a href="http://twitter.com/angular/fire"><strong><em>@angular/fire</em></strong></a><strong><em> </em></strong>in our project and check how angular.json is changed:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/891/1*PEEC85oUafShhvBJ50T6bQ.png" /><figcaption>deploy builder was added</figcaption></figure><p>As you can see — a new deploy builder was added (so we can do <em>ng deploy</em> now for our project to upload bundled files to FireBase hosting).</p><h4>Angular CLI standard builders</h4><p>As you can see from picture above — standard Angular CLI builders are located in <strong>@angular-devkit</strong> package which contains <em>build-angular</em> collection.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kI0erIThEY1l6MJOjep2Xw.png" /></figure><p>Here you can find all builders like build, karma, browser, dev-server, etc and their implementations.</p><h4><strong><em>Custom builders</em></strong></h4><p>Also, you can create your own builder for custom purposes:</p><ol><li><strong><em>To add extra Webpack config options </em></strong><em>(</em><a href="https://github.com/just-jeb/angular-builders/tree/master/packages/custom-webpack"><em>custom-webpack builders</em></a><em> by</em><strong><em> </em></strong><a href="https://medium.com/u/8169dbace738"><strong><em>JeB Barabanov</em></strong></a><strong><em> </em></strong><em>)</em></li><li><strong><em>Concat bundled JS files </em></strong><em>(</em><a href="https://github.com/manfredsteyer/ngx-build-plus"><em>ngx-build-plus</em></a><em> builder by</em><strong><em> </em></strong><a href="https://medium.com/u/acb3f1945710"><strong><em>Manfred Steyer</em></strong></a><em>)</em></li><li><strong><em>Automate other routine tasks for you </em></strong><em>(</em><a href="https://twitter.com/SantoshYadavDev/status/1246861985901211654"><em>configure and run</em><strong><em> </em></strong>source-map-explorer</a> — by <a href="https://medium.com/u/c0dd31a08f42">Santosh Yadav</a>)</li></ol><h4>More to read</h4><ol><li><a href="http://Angular CLI builders">Angular CLI builders (official doc)</a></li><li><a href="https://medium.com/angular-in-depth/angular-cli-under-the-hood-builders-demystified-v2-e73ee0f2d811">Angular CLI under the hood — builders demystified</a> by <a href="https://medium.com/u/8169dbace738">JeB Barabanov</a></li><li><a href="https://angular-builders.dev/home">Custom Angular builders list page</a> by <a href="https://medium.com/u/c0dd31a08f42">Santosh Yadav</a></li></ol><h4>Conclusion</h4><p>Builders are used to do some routine tasks: build code, run lint, run unit tests, deploy code to host-provider. Also, you can create your own builders to automate some operations and add some new possibilities: add Webpack configs, run scripts, concatenate bundled files, etc.</p><p>Like this article? <a href="https://buymeacoffee.com/nqypxi6"><strong>Buy me a coffee</strong></a> :-)</p><h3>Schematics</h3><p>Schematics transform your project: update files, install packages, add new component/modules/directives/etc files.</p><blockquote>The <a href="https://angular.io/guide/glossary#cli">Angular CLI</a> uses schematics to apply transforms to a web-app project (modify or create project files).</blockquote><blockquote>Schematics are run by default by the commands <strong>ng generate</strong>, and <strong>ng add</strong>.</blockquote><blockquote>If you create a new version of your library that introduces potential breaking changes, you can provide an <em>update schematic</em> to enable the <strong>ng update</strong> command to automatically resolve any such changes in the project being updated (to do automatically changes in project code so code uses new API).</blockquote><blockquote><a href="https://angular.io/guide/schematics">https://angular.io/guide/schematics</a></blockquote><p>and</p><blockquote>Schematics is a workflow tool for the modern web; it can apply transforms to your project, such as create a new component, or updating your code to fix breaking changes in a dependency. Or maybe you want to add a new configuration option or framework to an existing project</blockquote><blockquote><a href="https://blog.angular.io/schematics-an-introduction-dc1dfbc2a2b2">Schematics — An Introduction</a></blockquote><p>Ok, too vague as for me. Let&#39;s make it more specific.</p><p>Do you remember how we added the possibility to deploy to FireBase hosting in the previous section with <strong><em>ng add </em></strong><a href="http://twitter.com/angular/fire"><strong><em>@angular/fire</em></strong></a><strong><em> </em></strong>command? We use schematics.</p><p>What did this schematics do for us?</p><ol><li>Installed packages <strong><em>@angular/fire</em></strong>, <strong><em>firebase</em></strong>, <strong><em>firebase-tools</em></strong> and so on (package.json is updated also). <strong><em>@angular/fire</em></strong> contains a builder for deploying bundled code to FireBase.</li><li>Asked some specific questions while running (to specify options)</li><li>Updated angular.json — added <strong><em>deploy</em></strong> builder config. Now we can run <strong>ng deploy.</strong></li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9UfTf5dizgcOp0gOtpHt4Q.png" /></figure><p>So schematics did all preparations work for us to start using <strong><em>deploy</em></strong> builder: installed packages and updated configs.</p><h4>So how to run schematics</h4><ul><li><strong>ng new</strong> &lt;appName&gt; and <strong>ng generate</strong> &lt;unitType&gt; &lt;unitName&gt;— start respective schematic (using default schematics collection from @schematics/angular package). <br>You can specify some default options for them in angular.json file — see more details <a href="https://indepth.dev/overriding-angular-schematics/">here</a>. Or you can set schematics options <a href="https://angular.io/cli/generate">as a common line argument</a>.<br>You can override default schematics collection used by <strong><em>ng</em></strong> commands — modify project <em>angular.json</em> file (angular.json &gt; cli &gt; defaultCollection).</li><li><strong>ng add</strong> &lt;packageName&gt; — installs the package and then starts <a href="https://github.com/angular/components/blob/master/src/material/schematics/collection.json">ng-add schematics</a> from it (specified in its <a href="https://github.com/angular/components/blob/master/src/material/package.json#L36">package.json</a>).</li><li><strong>ng update</strong> &lt;packageName&gt; — installs a newer version of a package and then starts <a href="https://github.com/angular/components/blob/master/src/material/schematics/migration.json">migration schematics</a> from it (specified in <a href="https://github.com/angular/components/blob/master/src/material/package.json#L37">package.json</a>).</li><li>You can also run schematics with <strong><em>schematics</em></strong> command <br>(watch more details in this video: <a href="https://www.youtube.com/watch?v=X06tuCohJPQ">A Schematic Odyssey</a> by Kevin Schuchard &amp; Brian Love)</li></ul><p>*Here is a nice review of angular.json structure: &quot;<a href="https://nitayneeman.com/posts/understanding-the-angular-cli-workspace-file/">Understanding the Angular CLI Workspace File</a>&quot; by <a href="http://twitter.com/nitayneeman">@nitayneeman</a>.</p><h4><strong>Where are standard Angular schematics located?</strong></h4><p>You may already know that you can generate a set of component files with the command: <strong>ng generate component some-component</strong></p><p>Where are these files templates are take from? You can find them in @schematics package:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-ccQ9EdBOSQmWmKQLwwjKw.png" /></figure><p>So <strong><em>ng generate</em></strong> command just runs <strong><em>component</em></strong> schematics. Same with other schematics: directive, pipe, module, etc</p><h4><strong>Takeaway</strong></h4><ul><li>In Angular builders are used to do some routine tasks: build code, run lint, run unit tests, deploy code to host-provider.</li><li>Schematics transform your project: update files, install packages, add new component/modules/directives/etc files.</li><li>If you want to create your builder — deliver it with schematics (to be used with <strong>ng add</strong>) that update angular.json (add that builder into) and installed respective packages. <br>A nice example of such a package is &quot;@angular/fire”. It contains both builder (<a href="https://github.com/angular/angularfire/blob/be0a1fb97359659fa67b5b3a01af1d984f12cd3f/src/core/builders.json">here</a> and <a href="https://github.com/angular/angularfire/blob/882b254f9c3bcf406b38f876367500f5d282cd29/src/schematics/deploy/builder.ts">here</a>) and schematics for ng add (<a href="https://github.com/angular/angularfire/tree/master/src/schematics">here</a>)</li></ul><p>I will not stop on implementation details here. If you are interested — here is a list of recommended articles to start.</p><h4>More to read</h4><ol><li><a href="https://angular.io/guide/schematics">Generating code using schematics</a></li><li><a href="https://blog.angular.io/schematics-an-introduction-dc1dfbc2a2b2">Schematics — An Introduction</a></li><li><a href="https://medium.com/angular-in-depth/effective-automated-scaffolding-with-angular-schematics-c61d6640b7d5">Effective automated scaffolding with Angular Schematics</a></li><li><a href="https://indepth.dev/overriding-angular-schematics/">Overriding Angular Schematics</a></li></ol><p>5. <a href="https://github.com/angular-schule/ngx-deploy-starter">ngx-deploy-starter</a> — create your own deploy builder (and schematics)</p><h3>Custom <strong>tslint/eslint</strong> rules for Angular</h3><p>What is tslint for? It shows whether developers respect code style guide. Why do we need that? Style guide increases code readability (and so maintainability). And also there are rules that help you to prevent specific bugs (like <a href="https://github.com/cartant/rxjs-tslint-rules">rxjs-tslint-rules</a>).</p><p>You can install and then update project&#39;s tslint.json file to start using newly installed rules. For example</p><pre>npm install rxjs-tslint-rules --save-dev</pre><pre>//Update your tslint.json file to extend this package:</pre><pre>{<br>  &quot;extends&quot;: [<br>    &quot;rxjs-tslint-rules&quot;<br>  ],<br>  &quot;rules&quot;: {<br>    &quot;rxjs-add&quot;: { &quot;severity&quot;: &quot;error&quot; },<br>    &quot;rxjs-no-unused-add&quot;: { &quot;severity&quot;: &quot;error&quot; }<br>  }<br>}</pre><pre>*Taken from: <a href="https://github.com/cartant/rxjs-tslint-rules/blob/master/README.md">https://github.com/cartant/rxjs-tslint-rules/blob/master/README.md</a></pre><p>So when you run <strong>ng lint</strong> command :</p><ol><li>Angular CLI checks for lint builder in angular.json file, create it and run it.</li><li>Builder starts tslint</li><li>tslint read tslint.json file to grab all the rules and then checks your code for compliance with the rules.</li></ol><p>I will not dive in details HOW to create custom <em>tslint</em> rules here since it is out of the article scope, but you can read about it in the proposed articles below.</p><h4>Conclusion</h4><ul><li>In Angular builders are used to do some routine tasks: build code, run lint, run unit tests, deploy code to host-provider.</li><li>Schematics transform your project: update files, install packages, add new component/modules/directives/etc files.</li><li><strong><em>lint</em></strong> builder starts <em>tslint</em> which loads rules and then check your code to obey these rules — and you can create custom ones to expend for your code-style.</li><li>You don&#39;t need Angular CLI to start <em>tslint</em> through lint builder — you can run it directly with <strong><em>tslint</em></strong> command (if it is installed globally) or <strong><em>npx tslint</em></strong> (if <em>tslint</em> is installed only locally in your project)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZX1UC2gYWxxXl_FOU6WsIw.png" /><figcaption>ng lint uses tslint, and you can start it directly</figcaption></figure><h4>More to read</h4><ol><li><a href="https://medium.com/@phenomnominal/custom-typescript-lint-rules-with-tsquery-and-tslint-144184b6ff2d">Custom TSLint rules with TSQuery</a></li><li><a href="https://timdeschryver.dev/blog/migrating-a-tslint-rule-to-eslint-with-typescript-eslint">Migrating a TSLint Rule to ESLint</a></li><li><a href="https://github.com/cartant/rxjs-tslint-rules">rxjs-tslint-rules</a></li><li><a href="https://medium.com/@andrey.igorevich.borisov/writing-custom-tslint-rules-from-scratch-62e7f0237124">Writing custom TSLint rules from scratch</a></li><li><a href="https://hackernoon.com/custom-tslint-rules-easier-than-you-think-1bd9c361d70c">Custom TSLint rules — easier than you think</a></li></ol><blockquote>Did you know that:</blockquote><blockquote>👉 TSLint is getting deprecated</blockquote><blockquote>👉 The angular-eslint project is a port from codelyzer</blockquote><blockquote>👉The eslint-plugin-rxjs is a port from rxjs-tslint-rules</blockquote><blockquote>Read more in “<a href="https://timdeschryver.dev/blog/migrating-a-tslint-rule-to-eslint-with-typescript-eslint">Migrating a TSLint Rule to ESLint</a>” by <a href="https://medium.com/u/802a7996f6b6">Tim Deschryver</a></blockquote><h3><em>Custom </em>Typescript Transformers</h3><blockquote>The Angular CLI uses the AngularCompilerPlugin to transpile typescript. It is a webpack plugin that uses the typescript compiler together with various Typescript transformers to transpile the typescript to workable JS code for the browser.</blockquote><blockquote><a href="https://medium.com/@d.kingma?source=post_page-----7f4150797e05----------------------">David Kingma</a>. <a href="https://medium.com/joolsoftware/custom-typescript-transformers-with-angular-cli-7f4150797e05">Custom Typescript Transformers with Angular CLI</a></blockquote><p>Now let&#39;s show its place in a big picture:</p><ul><li>we start ng build</li><li>Angular CLI(<em>ng</em>) finds in angular.json respective builder</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sXi64aVREJ3oZdF8Nvg4Fw.png" /><figcaption>@angular-devkit/build-angular:browser builder</figcaption></figure><ul><li>the builder starts webpack, and webpack uses <a href="https://github.com/angular/angular-cli/blob/c7c574affba46efeb68f7ab268ace8f58e90a6b4/packages/ngtools/webpack/src/angular_compiler_plugin.ts">AngularCompilerPlugin</a>.</li><li>AngularCompilerPlugin starts typescript compiler (to compile project .ts files) and provide specific transforms also to this compiler (you can read more about it <a href="https://medium.com/angular-in-depth/do-you-know-how-angular-transforms-your-code-7943b9d32829">here</a>)</li><li>And you can provide additional typescript transforms too (👈🏽 we are here)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QjCEOCe2FwfjEEVemgFEtA.png" /><figcaption>Photo by <a href="https://unsplash.com/@ellladee?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">N.</a></figcaption></figure><p><strong>What do standard AngularCompilerPlugin transforms do?</strong></p><p>For example — transform &quot;<em>Inline resource</em>&quot;- takes component decorator templateUrl value (a filename), read the file, and put <em>template </em>prop instead<em> </em>with file content as a value<em>.</em></p><p>A list of transformers is located <a href="https://github.com/angular/angular-cli/tree/c7c574affba46efeb68f7ab268ace8f58e90a6b4/packages/ngtools/webpack/src/transformers">here</a>.</p><p>You can read about many of them in the nice article of <a href="https://medium.com/u/d59a9e801370">Alexey Zuev</a> &quot;<a href="https://medium.com/angular-in-depth/do-you-know-how-angular-transforms-your-code-7943b9d32829">Do you know how Angular transforms your code?</a>&quot;.</p><p><strong>Why may we need to code our own custom transforms?</strong></p><ol><li>You want to apply your own syntax in Angular templates, and to make Angular understand it. <a href="https://github.com/typebytes/ngx-template-streams">ngx-template-streams</a> uses that approach (<a href="https://www.youtube.com/watch?v=zaCYMarOtDE">video</a>).</li><li>You want to grab some specific information from one file and modify another file during build process (Here is an <a href="https://levelup.gitconnected.com/writing-a-custom-typescript-ast-transformer-731e2b0b66e6">article</a> that describes such example)</li><li>You want to find all RxJS observables in your Angular project and automatically insert <em>unsubscribe</em> code (a nice <a href="https://medium.com/angular-in-depth/having-fun-with-angular-and-typescript-transformers-2c2296845c56">article</a> from <a href="https://medium.com/u/412893d0911f">Christian Janker</a> about that).</li><li>etc</li></ol><p>How do we provide our custom transformer so the typescript compiler can apply it?</p><p><em>AngularCompilerPlugin</em> has a _<a href="https://github.com/angular/angular-cli/blob/c7c574affba46efeb68f7ab268ace8f58e90a6b4/packages/ngtools/webpack/src/angular_compiler_plugin.ts#L104"><em>transformers</em></a> property where all Angular standard transformers are usually added to. So we have just to modify it and add also our own customer transformer. How?</p><p>There is a special <em>ngx-build-plus:browser</em> builder (it replaces standard builder for <strong>ng build </strong>command) from <a href="https://github.com/manfredsteyer/ngx-build-plus">ngx-build-plus</a> (by <a href="https://medium.com/u/acb3f1945710">Manfred Steyer</a>). This <em>ngx-build-plus:browser </em>that allows to modify internal webpack configuration in Angular projects (you remember, that <em>ng build</em> runs a builder that starts webpack to build our project, right?:)</p><p>Since builder has instance of webpack so you can provide your <a href="https://github.com/manfredsteyer/ngx-build-plus#using-plugins">plugin</a> for this builder and can get access to <em>AngularCompilerPlugin </em>instance and modify its <em>_transformers</em> prop by adding your custom transformer to the list.</p><p>Lets better describe it step by step:</p><ol><li>We install <a href="https://github.com/manfredsteyer/ngx-build-plus">ngx-build-plus</a> and now when we start <em>ng build</em> command new builder is used — <em>ngx-build-plus:browser (previously it was standard Angular CLI builder — @angular-devkit/build-angular:browser)</em></li><li><em>ngx-build-plus:browser can accept our plugin (it is not TS transformer, but webpack config transformer) where you can modify webpack config (Here is an </em><a href="https://gist.github.com/meDavid/73608a3c813a4c3ca0175707a4b82303#file-ng-ts-register-transformer-ts"><em>example of such plugin</em></a><em>).</em></li><li>So your webpack-modifier plugin gets access to <em>AngularCompilerPlugin </em>instance and modify its <em>_transformers</em> prop by adding your custom transformer to the list.</li><li>After <em>ngx-build-plus:browser </em>applied you webpack-config-changer plugin and get modifier webpack config — it runs webpack build or your project.</li><li>During the build process, webpack applies <em>AngularCompilerPlugin </em>transformers (and your transformer also among other transformers) — here is an <a href="https://gist.github.com/meDavid/73608a3c813a4c3ca0175707a4b82303#file-ng-ts-register-transformer-ts">example of such dummy transformer</a> by <a href="https://medium.com/@d.kingma?source=post_page-----7f4150797e05----------------------">David Kingma</a>.</li></ol><p>Phew 🤓</p><h4>More to read</h4><ol><li><a href="https://medium.com/angular-in-depth/having-fun-with-angular-and-typescript-transformers-2c2296845c56">Having fun with Angular and Typescript Transformers</a></li><li><a href="https://www.youtube.com/watch?v=zaCYMarOtDE">Hacking the Angular compiler with your own syntax [Video]</a></li><li><a href="https://medium.com/joolsoftware/custom-typescript-transformers-with-angular-cli-7f4150797e05">Custom Typescript Transformers with Angular CLI</a></li><li><a href="https://medium.com/angular-in-depth/do-you-know-how-angular-transforms-your-code-7943b9d32829">Do you know how Angular transforms your code?</a></li><li><a href="https://medium.com/angular-in-depth/converting-typescript-decorators-into-static-code-using-tsquery-tstemplate-and-transforms-8c65d606a517?source=---------4------------------">Converting TypeScript decorators into static code using tsquery, tstemplate and transforms!</a></li><li><a href="https://levelup.gitconnected.com/writing-a-custom-typescript-ast-transformer-731e2b0b66e6">Writing a Custom TypeScript AST Transformer</a></li><li><a href="https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API">Using the Compiler API</a></li></ol><h3>Conclusion</h3><p>Let&#39;s go through all parts once more (yes, you guessed right, I was a teacher many years ago, and my mom too 😁):</p><ul><li><strong>Builders</strong> are used to start some routine tasks: build code, run lint, run unit tests, deploy code to host-provider.</li><li><strong>Schematics</strong> transform your project: update files, install packages, add new component/modules/directives/etc files.</li><li><strong><em>lint</em></strong> builder starts <em>tslint</em> which loads rules and then check your code (typescript parser is used) to obey these rules — and you can create custom ones to expend for your code-style.</li><li>To build angular code (which contains angular specific template constructs: *ngIf, [somePros], (click), etc) webpack uses <em>AngularCompilerPlugin. </em>It<em> </em>transforms Angular syntax converting it to something that the TypeScript compiler can understand. You can create your own template syntax (or typescript code syntax) for some purposes and make Angular (webpack) understand it too by providing <strong>custom transformer</strong>. <br>Or you can use the transformer just to add something to code or modify it at build time.<br>Transformers are applied only during build time.</li></ul><h4>AST Conclusion</h4><p>Many of reviewed entities of Angular CLI (or related apps) uses Abstract Syntax Tree to do their work. Not to be embarrassed let&#39;s clarify that part too.</p><p>Typescript compiler parser can represent each .ts file as AST.</p><p>A <a href="https://github.com/palantir/tslint/blob/a66f3b3430b4d3f03ff93fcef94609b88809d4e1/src/linter.ts#L142">linter scans</a> an Abstract Syntax Tree (AST). Each specific <a href="https://github.com/palantir/tslint/blob/master/src/rules/arrayTypeRule.ts#L63">lint-rule</a> implementation is used to <a href="https://github.com/palantir/tslint/blob/a66f3b3430b4d3f03ff93fcef94609b88809d4e1/src/linter.ts#L208">look for patterns in code</a> (actually in AST).</p><p>Typescript transformers use file(s) AST also to look through and update files.</p><p>Schematics operates over project filesystem representation Tree (or Source)- you modify Tree and then these changes are applied to filesystem. (Tree is not typescript parser AST, read more <a href="https://medium.com/angular-in-depth/effective-automated-scaffolding-with-angular-schematics-c61d6640b7d5">here</a>)</p><p>Builders don&#39;t use AST at all. <a href="https://github.com/angular/angular-cli/blob/v9.1.3/etc/api/angular_devkit/architect/src/index.d.ts#L29">BuilderHandler</a> (builder implementation function) only gets input params and some architectural context (<a href="https://github.com/angular/angular-cli/blob/v9.1.3/etc/api/angular_devkit/architect/src/index.d.ts#L8">BuilderContext</a>). BuilderContext just contains some project related information (<em>ProjectMetadata</em>, <em>currentDirectory</em>, <a href="https://github.com/angular/angular-cli/blob/v9.1.3/etc/api/angular_devkit/architect/src/index.d.ts#L8">etc</a>).</p><p><strong>Homework</strong></p><p>Relax, just kidding you, no homework. Go feed your bear, play with your nuclear reactor, and don&#39;t forget to drink vodka (joking 😁).</p><p>This article is a part of my own learning process. If you found some wrong or partially wrong statement — feel free to correct me in comments.</p><p>Let’s keep in touch on <a href="https://twitter.com/El_Extremal"><strong>Twitter</strong></a>!</p><p>Cheers!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9ed1a0d1930" width="1" height="1" alt=""><hr><p><a href="https://medium.com/angular-in-depth/angular-cli-flows-big-picture-9ed1a0d1930">Angular CLI flows. Big picture.</a> was originally published in <a href="https://medium.com/angular-in-depth">Angular In Depth</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[5 things I miss after switching from Windows laptop to MacBookPro.]]></title>
            <link>https://medium.com/@alexanderposhtaruk/5-things-i-miss-after-switching-from-windows-laptop-to-macbookpro-b925be3abcf7?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/b925be3abcf7</guid>
            <category><![CDATA[mac]]></category>
            <category><![CDATA[windows]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Sat, 11 Apr 2020 10:47:47 GMT</pubDate>
            <atom:updated>2020-04-11T10:47:47.394Z</atom:updated>
            <content:encoded><![CDATA[<p><em>And how I tried to substitute missed functionality</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BrjKN06gFYZJOFQFXFPxXw.png" /><figcaption>Windows 10 vs MacBookPro</figcaption></figure><p>A while ago I was employed and what was new for me — I got MacBookPro as a work laptop. It was a new experience for me (since the last 20 years I&#39;ve been using only Windows 95/../10).</p><p>Whilst overall I am quite satisfied with the Mac experience, but I have a few small things that I still miss on Mac that was possible on Windows.</p><p>Feel free to advise your favorite Mac apps that may cover these gaps — I would appreciate it.</p><p>So let&#39;s go 😎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*Zq9MkHRGSE-45rhm7AWTtA.jpeg" /><figcaption>When you ask the community how to do that on Mac :-D</figcaption></figure><h4>#1. I cannot stretch the application window on two screens.</h4><p>I often work with Chrome Dev Tools. And on Windows computer I placed DelTools panel to the right and stretched window so panel appeared on another monitor.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DoJUa4sexU71jSmr2kWtVA.png" /><figcaption>One window stretched to two screens</figcaption></figure><p>Unfortunately, I didn&#39;t find the way to do this on Mac. The only solution is to open Chrome Dev Tools as a separate window and then move DevTools window to another monitor screen. But it is not the same experience.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y3GYcGXdbipZ_5VZ13FoQQ.gif" /><figcaption>Chrome Dev Tools as separate window</figcaption></figure><h4>#2. There is no shortcut ‘Minimize all windows’ 😢</h4><p>There are often many open apps on my screen. Some sometimes I just want to minimize them all and open the one I need. But there is no &#39;Minimize all windows (Show Desktop)&#39; button like on Windows 7 taskbar. I miss it a lot.</p><p>I did find such a button in Windows 10 but:</p><blockquote>&quot;In Windows versions prior to Windows 7, there was a button which minimized all opened Windows and showed the desktop. In Windows 10, there is no such button. Instead, to minimize all open windows and show the Desktop, you need to move the mouse pointer to the right edge of the taskbar (or the bottom edge if your taskbar is vertical) and click a tiny invisible button.&quot;</blockquote><blockquote><a href="https://winaero.com/blog/add-the-show-desktop-button-next-to-start-in-windows-10/">Add the Show Desktop button next to Start in Windows 10</a></blockquote><p>I didn&#39;t find such a shortcut in macOS. But to archive a bit similar experience you can use:</p><ol><li>Use Mission Control button to see all open windows and switch between them</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WJbqulV-9NQpIhO828M6Yw.png" /><figcaption>Mission Control button</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gKWMQqQVC9l-J4ViuwIx-w.png" /><figcaption>How apps look after pressing Mission Control</figcaption></figure><p>2. Show Mac Desktop with <em>Command + F3</em><br>But it doesn&#39;t work the same way as in Windows. Yes, you see the Desktop but once you press any app on a Dec — all the apps windows will return to its screen positions. While in Windows they become minimized and you can maximize the app you want to work with.</p><h4>#3. Dock only visible on one screen at the moment</h4><p>In Windows, you see the taskbar on each screen while in MacOS you have to choose on which screen you have it. Maybe for someone, it is a benefit but for me, it is not convenient to make it appear each time I need to maximize other apps.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/942/1*tjmNO5GOh-E3_-MmOiFNaw.png" /><figcaption>You should choose the screen for Dec to be shown</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/410/0*bpyGn-sxHe_6dNqv.png" /><figcaption>You can adjust taskbar multi-display appearance</figcaption></figure><h4>#4. No &#39;Delete&#39; button on a keyboard</h4><p>Actually there is but it works as BackSpace on PC (deleting character before the cursor).</p><p>You can achieve this behavior by pressing <strong><em>fn+Delete</em></strong> button but not the same experience.</p><h4>#5. PuntoSwitcher (keyboard layout input language correction) works for 2 languages only</h4><p>I am a native speaker for Ukrainian and Russian language (what is quite usually in Ukraine). And also uses the English language quite often too. Sometimes if you forget to switch languages — you get some abracadabra (Jabberwocky) text.</p><p>Not to erase it and then input again I used to use <a href="https://yandex.ru/soft/punto/">PuntoSwitcher</a>. It works quite good and by hotkey press changes language layout for last edited work so you don&#39;t have to re-type it again:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/578/1*LH-vJMb9O7wTdsMj9ub6FA.gif" /><figcaption>Press Shortkey (Option for Mac, Break for Windows) and Punto will change keyboard layout for the last word entered</figcaption></figure><p>If you have 3 languages — then in windows just press twice to convert to a language you need. But not on Mac — you cannot convert to language 1, then 2 and then 3 — just 1 and 2. And you should choose in options (in Punto for Mac) which two languages you will be able to switch between 😭.</p><h4>Conclusion</h4><p>Well. All these imperfections are not a disaster for me. And after being fired from my last job I still continue using MacBookPro (I was allowed to buy my work laptop for a good price). But these are things I definitely would like to have:-) Do you have such staff you miss too? Share in comments!</p><p>BTW usually I write articles on Angular and RxJS! So if you are interested -let’s keep in touch on <a href="https://twitter.com/El_Extremal"><strong>Twitter</strong></a><strong>.</strong></p><p>Cheers!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b925be3abcf7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ngRx/Store and 5 silly mistakes]]></title>
            <link>https://itnext.io/ngrx-store-and-5-silly-mistakes-780ae2f961d0?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/780ae2f961d0</guid>
            <category><![CDATA[ngrx]]></category>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Tue, 03 Mar 2020 13:02:02 GMT</pubDate>
            <atom:updated>2022-03-23T11:02:58.681Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Learn how to avoid them</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p-m8joe-7CQY9aXyoc2K3w.jpeg" /><figcaption>Better spend time with family than in debugging:-)</figcaption></figure><p><em>In this article, I will tell you how to avoid simple pitfalls while working with ngRx to improve your family-work balance:-)</em></p><h4>Introduction</h4><p>If you work with Angular you definitely know about the most popular state management system for Angular applications <a href="https://ngrx.io/"><strong>ngRx/Store</strong></a>.</p><p>Let&#39;s recall what it is to be on the same page:</p><blockquote><strong>@ngrx/store</strong></blockquote><blockquote>… is RxJS powered state management for Angular applications, inspired by Redux. Store is a controlled state container designed to help write performant, consistent applications on top of Angular.</blockquote><p>Here is the flow diagram from official documentation describing how it works:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*F1zvhLBSOjf9HVRa.png" /></figure><p>Components send <strong>Actions</strong> that is an object with a mandatory property <em>type </em>and optionally other properties with data to be stored to the central object — a Store. Actions are being handled by <strong><em>reducers</em></strong><em>, </em>a special functions that make data from actions and put them to Store (or modify Store). Other components can subscribe to Store updates (to a specific part of the Store) with <strong>selectors</strong> (actually selectors determines which part of the Store updates you want to monitor). In simple cases, selectors receive state object as an argument and return you some property of the state object:</p><pre>(state) =&gt; state.prop1</pre><p>Before I start to keep your time let&#39;s review the simplest example. Not to re-invent the bicycle I will use the simplified example from the <a href="https://ngrx.io/guide/store">official doc</a>.</p><p>We will create a Store with counter value, and action/reducer that increases that counter value.</p><p>Let&#39;s create an action:</p><pre>// src/app/counter.actions.ts</pre><pre><a href="https://ngrx.io/api/store-devtools/DevToolsFeatureOptions#import">import</a> { <a href="https://ngrx.io/api/store/createAction">createAction</a> } from &#39;@ngrx/store&#39;;</pre><pre><a href="https://ngrx.io/api/store-devtools/DevToolsFeatureOptions#export">export</a> const increment = <a href="https://ngrx.io/api/store/createAction">createAction</a>(&#39;[Counter Component] Increment&#39;);</pre><p>Reducer:</p><pre>//src/app/counter.reducer.ts</pre><pre>import { createReducer, on } from &#39;@ngrx/store&#39;;<br>import { increment } from &#39;./counter.actions&#39;;</pre><pre>export const initialState = 0;<br>const _counterReducer = createReducer(initialState,<br>   on(increment, state =&gt; state + 1)<br>);</pre><pre>export function counterReducer(state, action) {<br>   return _counterReducer(state, action);<br>}</pre><p>Add StoreModule module to app.module.ts</p><pre>import { BrowserModule } from &#39;@angular/platform-browser&#39;;<br>import { NgModule } from &#39;@angular/core&#39;;<br>import { AppComponent } from &#39;./app.component&#39;;</pre><pre>import { StoreModule } from &#39;@ngrx/store&#39;;</pre><pre>import { counterReducer } from &#39;./counter.reducer&#39;;</pre><pre>@NgModule({<br>  declarations: [AppComponent],<br>  imports: [<br>    BrowserModule,<br>    StoreModule.forRoot({ count: counterReducer })<br>  ],<br>  providers: [],<br>  bootstrap: [AppComponent],<br>})</pre><pre>export class AppModule {}</pre><p>You can see that we specify our reducer in this line</p><pre>StoreModule.forRoot({ count: counterReducer })</pre><p>To read count value we just need to use <a href="https://ngrx.io/api/store/select"><em>select</em></a> function in a component:</p><pre>// in some component<br>import { Store, select } from &#39;@ngrx/store&#39;<br>...</pre><pre>constructor(private store: Store&lt;{ count: number }&gt;) {</pre><pre>this.count$ = store.pipe(<strong>select</strong>(&#39;count&#39;)); <br>// now we have observable that will emit values on each count update</pre><pre>// old school approach<br>//this.count$ = store.pipe(<strong>select</strong>(state =&gt; state.count));</pre><pre>}</pre><p>What if we don&#39;t want to keep count in the main app module?<br>We can put it to a feature module.</p><pre>@NgModule({<br>  declarations: [AppComponent],<br>  imports: [<br>    BrowserModule,<br>    StoreModule.forRoot({}),<br>    StoreModule.forFeature(&#39;featureName&#39;, { count: counterReducer })<br>  ],<br>  providers: [],<br>  bootstrap: [AppComponent],<br>})</pre><pre>export class AppModule {}</pre><p>Now our selector to grab the value from feature-branch of the Store state will be:</p><pre>// count.selectors.ts<br>export const selectFeature = createFeatureSelector&lt;FeatureState&gt;(&#39;featureName&#39;);</pre><pre>export const countSelector = createSelector(selectFeature, (state) =&gt; state.count);<br></pre><pre>// And in some component<br>this.count$ = store.pipe(<strong>select</strong>(countSelector));</pre><p>Now let&#39;s check how all this works:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/834/1*m_qx6oiKiKYvBCxghwtqJg.gif" /><figcaption>One component increments state.count, another component displays state value.</figcaption></figure><p>All our actions and Store state changes we can observe with nice Chrome plugin: <a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ru">Redux DevTools</a>:<br>1. Install a plugin in Chome<br>2. install <a href="https://ngrx.io/api/store-devtools">@ngrx/store-devtools</a> module to your Angular app:<br> — <strong>ng add @ngrx/store-devtools</strong> <br>- Or <strong><em>npm i @ngrx/store-devtools</em></strong> (in that case you should add StoreDevtoolsModule to AppModule manually)</p><p>3. monitor your Store in Chrome Dev Tools (Redux tab)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HF8QV6G3oodF_PUilE4OTw.gif" /></figure><p>Simple, right?</p><p>At that place, you may ask yourself why do we need an article that just represents the official documentation example? Because even with these simple flows you can spend hours on debugging if something doesn&#39;t work as expected.</p><p><strong>I revealed 5 often mistakes in my (and my fellow developers) practice.</strong></p><h4><strong>#1. Redux-DevTools doesn&#39;t display undefined props in actions</strong></h4><p>Say we have an action that sends not the only type of message but also some additional info:</p><pre>{<br> type: SOME_TYPE,<br> value: this.someProp<br>}</pre><p>For that purpose lets modify a bit our code:</p><pre><strong>// counter.actions.ts</strong><br>...<br>export const increment = createAction(&#39;[Counter Component] Increment&#39;, props&lt;{value: number}&gt;());</pre><pre><strong>// counter.reducer.ts</strong><br>const counterReducerFunc = createReducer(initialState,<br>  on(increment, (<em>state</em>, {<em>value</em>}) =&gt; <em>state </em>+ 1 + <em>value</em>)<br>);</pre><pre><strong>//app.component.ts</strong><br>public value;<br>...</pre><pre>increment() {<br>// provide additional value to actionCreator function  this.store.dispatch(increment({value: this.value})); <br>}</pre><p>Now our reducer should increase state value by 1 and add <em>value.</em></p><p>But, something goes wrong and you want to debug the actions in Redux Dev Tools.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IJ4-k05xAchgK_0dfYz3rg.gif" /><figcaption>Debugging issue</figcaption></figure><p>Ok, <em>count</em> got NAN value, this is not correct. And why don&#39;t we see <em>value </em>prop in action tab content in ReduxDevTools? Only <em>type</em> field is present.</p><p>The answer is that <strong>a)</strong> we forgot to assign some number to <em>value</em> property, <strong>b)</strong> <strong><em>Chrome plugins cannot get undefined values</em></strong> since it cannot be stringified.</p><p>Let&#39;s assign <em>value</em> with 0<strong><em>.</em></strong></p><pre><strong>//app.component.ts</strong><br>public value = 0; // or other number value</pre><p>Now we can observe this prop in ReduxDevTools:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zuMWdDe3s6tMJRy-cS2grw.gif" /></figure><p>I spend an hour to reveal it. Maybe you will waste less time after reading this:)</p><p>You can take a look at code in <a href="https://github.com/kievsash/ngrx-store-and5sillyMistakes/tree/pitfall_1_ReduxDevTools_Doesnt_display_undefined">this branch of the article GitHub repo</a>. Or check it in a <a href="https://ng-run.com/edit/Zi5H99cHsvPsoDHFpble">ng-run.com playground</a>.</p><p><strong>Take away:</strong> better to use <strong>null</strong> if you need to specify empty value since null can be stringified and can be shown in ReduxDevTools.</p><h4><strong>#2. StoreDevModule may slow down the app</strong></h4><p>Once upon a time, I had a big list of objects in a Store. And some user operations modified data on the specific Action type and put them back to Store and then components displayed that data.</p><p>What our testers observed that starting from a few hundreds of items in a list each user operation caused small but noticeable UI update lags. And this was not rendering but JS issue.</p><p>After checking with ChromeDevTools on Performance tab (you can read more about it <a href="https://developers.google.com/web/tools/chrome-devtools/rendering-tools">here</a>) I got this picture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/562/1*fO3bfV3jShQmUiRHPxc8Cg.png" /><figcaption>7.8% of Javascript activity is grabbed by stringify function</figcaption></figure><p>Do you remember why it may happen? Yes, because we send data to our ReduxDevTools plugin to be able to monitor Store Actions and state.</p><p>Since we added StoreDevToolsModule manually to the AppModule — we missed option to turn it off for prod builds:</p><pre>imports: [<br>  StoreModule.forRoot({}),<br>  StoreModule.forFeature(featureKey, {items: itemsReducer}),<br>  BrowserModule,<br>  // StoreDevtoolsModule.instrument({ maxAge: 25, <strong>logOnly: environment.production</strong> }), <strong>// missed logOnly option</strong><br>  EffectsModule.forRoot([AppEffects])<br>],</pre><p>After I added it — UI started to feel much better:-)</p><p><strong>Takeaway:</strong> do not forget <strong>logOnly </strong>option when you use StoreDevtoolsModule to monitor your ngrx/Store activities. Actually, if you install it with <strong>ng add @ngrx/store-devtools</strong> then this option will be added automatically. You read more about <em>logOnly</em> <a href="https://stackoverflow.com/a/49106637/6528560">here</a>.</p><p>You can play with the code <a href="https://github.com/kievsash/ngrx-store-and5sillyMistakes/tree/pitfall_2_StoreDevtoolsModule_slow_down">in GitHub repo branch</a>. Or start this branch on <a href="https://ng-run.com/">ng-run.com</a> Angular playground by <a href="https://medium.com/u/d59a9e801370">Alexey Zuev</a>. For that just copy GitHub branch link and add ng-run.com/github/&lt;yourlink&gt; like this:</p><pre><strong>Branch link:</strong> <br>https://github.com/kievsash/ngrx-store-and5sillyMistakes/tree/pitfall_2_StoreDevtoolsModule_slow_down</pre><pre>Now lets start it on ng-run.com</pre><pre><a href="https://ng-run.com/github/kievsash/ngrx-store-and5sillyMistakes/tree/pitfall_2_StoreDevtoolsModule_slow_down">https://ng-run.com/github/<strong>kievsash/ngrx-store-and5sillyMistakes/tree/pitfall_2_StoreDevtoolsModule_slow_down</strong></a></pre><p>Like this article? <a href="https://buymeacoffee.com/nqypxi6"><strong>Buy me a coffee</strong></a> :-)</p><p><strong>#3. You import feature module but it doesn&#39;t work</strong></p><p><strong>a)</strong> Ok, So you have nice feature Angular module where you put:</p><pre>// feature.module.ts</pre><pre>...</pre><pre>imports: [<br>   StoreModule.forFeature(featureKey, {items: itemsReducer}),<br>...</pre><p>You expect it should work when you added it to app.module.ts AppModule imports. But… it doesn&#39;t) You open a ChromeDevTools console and see:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/717/1*HHzO9DIpC3hu77-mVP1zSw.png" /></figure><p>Ok, so we go to <a href="https://github.com/ngrx/platform/search?q=ReducerManager&amp;unscoped_q=ReducerManager">ngrx/platform GitHub repo</a> and search for &#39;<em>ReducerManager</em>&#39; entity. And see that it is provided as REDUCER_MANAGER_PROVIDERS by StoreModule.forRoot(…) call <a href="https://github.com/ngrx/platform/blob/8.6.0/modules/store/src/store_module.ts#L164">here</a>.<br>Answer is obvisous: we forgot to include StoreModule.forRoot({}) in out AppModule.</p><pre>// app.module.ts<br>imports: [<br>  StoreModule.forRoot({}),<br>  StoreModule.forFeature(featureKey, {items: itemsReducer}),</pre><p>Now it works good.</p><p><strong><em>b) </em></strong><em>I found one more interesting behavior but with StoreDevtoolsModule</em></p><p>Ok, so you added it to AppModule:</p><pre>imports: [<br>  StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production }),<br>  StoreModule.forRoot({}),<br>  StoreModule.forFeature(featureKey, {items: itemsReducer}),</pre><p>But when you open Redux tab in ChromeDevTools you see this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/737/1*Ska_gpHACxP_7o8O4c0VLA.png" /><figcaption>No Store found.</figcaption></figure><p>Why??</p><p>Because I just put StoreDevtoolsModule in <em>imports</em> array BEFORE StoreModule.forRoot. So it seems like Angular tries to instantiate it before any Store is created. Just put StoreDevtoolsModule AFTER StoreModule.forRoot in AppModule decorator <em>imports</em> array to fix the issue.</p><pre>imports: [<br>  StoreModule.forFeature(featureKey, {items: itemsReducer}),<br>  StoreModule.forRoot({}),<br>  StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production }),</pre><p>Now it works good:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/714/1*1ezAkljV6o4xcjknLBzOlw.png" /></figure><p>Interesting that in Angular 9 putting <em>StoreModule.forFeature</em> BEFORE StoreModule.forRoot doesn&#39;t create any <a href="https://github.com/ngrx/platform/issues/400">issue</a>.</p><p>You can find the code to play with <a href="https://github.com/kievsash/ngrx-store-and5sillyMistakes/tree/pitfall_3_StoreDevtoolsModule_before_StoreModule.forRoot">here</a>.</p><p><strong>#4. Exported reducer function is necessary as function calls are not supported by the AOT compiler (in Angular 8).</strong></p><p>The title of this pitfall sounds not clear but actually it is very simple. You have reducer:</p><pre>export const counterReducer = createReducer(initialState,<br>  on(increment, <em>state </em>=&gt; <em>state </em>+ 1),<br>  on(decrement, <em>state </em>=&gt; <em>state </em>- 1),<br>  on(reset, <em>state </em>=&gt; 0),<br>);</pre><pre>@NgModule({<br>  declarations: [],<br>  imports: [<br>    StoreModule.forRoot({ count: counterReducer })<br>  ],<br>  providers: [],<br>})<br>export class CounterStateModule { }</pre><p>And it works quite well…until we try to build the production code:</p><pre>ERROR in Error during template compile of &#39;CounterStateModule&#39;<br>  Function calls are not supported in decorators but &#39;createReducer&#39; was called in &#39;counterReducer&#39;<br>    &#39;counterReducer&#39; calls &#39;createReducer&#39; at app/counter.state.ts</pre><p>This is a well-know issue, you can read more about it <a href="https://angular.io/guide/aot-metadata-errors#function-calls-are-not-supported">here</a>.</p><p>Fortunately, when I tested it on Angular 9 project with Ivy (ngRx/Store 8.6) — it was already solved! You can read more details in this <a href="https://github.com/ngrx/platform/issues/2241">issue</a>.</p><p>You can check the code <a href="https://github.com/kievsash/ngrx-store-and5sillyMistakes/tree/pitfall4_no_need_to_export_function_for_reducer">here</a>.</p><p><strong>Takeaway:</strong> <a href="https://angular.io/guide/updating-to-version-9">update to Angular 9</a> 😎</p><p><strong>#5. Action creator is a function but if you forget to put parentheses — ngRx keeps silence.</strong></p><p>Here is a possible pitfall reproduce code:</p><pre>constructor(private store: Store&lt;{ count: number }&gt;) {<br>}<br><br>selectAll() {<br>  this.store.dispatch(select);//should be select() but no type error<br>}<br>unselectAll() {<br>  this.store.dispatch(unselect()); // correct<br>}</pre><p>Typescript will not help you here. But fortunately, you will find a hint in ChromeDevTools console:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/713/1*M2RdZeTXQl0fbSOR5Xmv6g.png" /></figure><p><strong>Takeaway:</strong> Do not put all eggs in typescript basket 🎓 Sometimes it may not help you.</p><h4>Conclusion</h4><p>Ok, so what we have learned in this article?</p><ol><li>Use <strong>null</strong> instead of <strong>undefined</strong> as <em>noValue</em> to be able to observe it in ReduxDevTools Chrome plugin.</li><li>StoreDevModule may slow down the app. To prevent it — set <em>logOnly </em>option as <em>true.</em></li><li>Do not forget to put StoreModule.forRoot({}) in AppModule when you connect other ngrx/Store feature modules.</li><li>Exported reducer function is not necessary in Angular 9.</li><li>Do not forget to put parentheses in your call action creator function.</li></ol><p>Now you can spend more time with your family but not in debugging sessions⛷</p><p>Let’s keep in touch on <a href="https://twitter.com/El_Extremal"><strong>Twitter</strong></a>!</p><blockquote>BTW. I started a video tutorial series “<a href="https://www.youtube.com/watch?v=HI5f6naJdJ8&amp;list=PLNadw4d8-KMVSOffiYBuOlzvF38sO9pdu&amp;utm_content=educational&amp;utm_campaign=2019-11-13&amp;utm_source=email-sendgrid&amp;utm_term=8565702&amp;utm_medium=2586716"><strong>Angular can waste your time</strong></a>” on Youtube. There I will be publishing videos about resolving tricky cases with Angular and RxJS. <a href="https://www.youtube.com/watch?v=HI5f6naJdJ8&amp;list=PLNadw4d8-KMVSOffiYBuOlzvF38sO9pdu&amp;utm_content=educational&amp;utm_campaign=2019-11-13&amp;utm_source=email-sendgrid&amp;utm_term=8565702&amp;utm_medium=2586716"><strong>Take a look!</strong></a></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/60/0*LVFcHyxCkdrmIg-T" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/594/0*_mYOWVBC882P8ClS.png" /></figure><p>Cheers!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=780ae2f961d0" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/ngrx-store-and-5-silly-mistakes-780ae2f961d0">ngRx/Store and 5 silly mistakes</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Angular ElementSchemaRegistry for “dummies”]]></title>
            <link>https://medium.com/angular-in-depth/angular-elementschemaregistry-for-dummies-83d54cd31478?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/83d54cd31478</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[angular-cli]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Fri, 07 Feb 2020 13:21:18 GMT</pubDate>
            <atom:updated>2022-03-23T11:04:20.287Z</atom:updated>
            <content:encoded><![CDATA[<p><em>How does Angular know I made an error in a component template?</em></p><p><em>Let&#39;s find out (CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, etc).</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ywk1dinx8SnUV17-10JFJg.jpeg" /><figcaption>Big brother watches your templates 😱</figcaption></figure><p><em>*Angular 8.2 source code is used for this article.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/743/0*gtefeYVtZrehu953.png" /></figure><blockquote><a href="https://medium.com/angular-in-depth"><strong><em>AngularInDepth</em></strong></a><strong><em> is moving away from Medium. More recent articles are hosted on the new platform </em></strong><a href="https://indepth.dev/angular/"><strong><em>inDepth.dev</em></strong></a><strong><em>. Thanks for being part of indepth movement!</em></strong></blockquote><p>I bet every Angular developer met such an error message in ChromeDevTools console:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/687/1*Y9s2dS4RqDflGu842TE_Jg.png" /><figcaption>&lt;some-custom-tag&gt;&lt;/some-custom-tag&gt;</figcaption></figure><p>Usually, this means you forgot to add a component to the module — so Angular doesn’t know anything about it.<br>Sometimes this error is shown on native HTML tags:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/675/1*b2ql9JwYe6xZuaHLW36GvA.png" /><figcaption>&lt;button data-someAttr<strong>=”</strong>{{someValue}}<strong>” </strong>&gt;<strong>Save</strong>&lt;/button&gt;</figcaption></figure><p>You can watch these errors by running article demo project <a href="https://github.com/kievsash/ElementSchemaRegistryDemo/tree/1_Watch_The_Error_in_console">here</a>.</p><p>Yes, we can solve it by using <strong>[attr.someAttr]=”someValue” </strong>notation, but how does Angular know that button doesn’t have such an attribute? And can we somehow omit Angular checking for our specific cases (custom elements out of Angular scope or some specific attributes)?<br>This is time to introduce <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/schema/element_schema_registry.ts"><strong>ElementSchemaRegistry</strong></a> from ‘<a href="https://github.com/angular/angular/tree/8.2.x/packages/compiler">@angular/compiler</a>’ package.</p><blockquote><strong><em>A more elaborate version of this article </em></strong><a href="https://indepth.dev/getting-inside-angulars-element-schema-registry-mechanism/"><strong><em>is available here</em></strong></a><strong><em>.</em></strong></blockquote><h3>Prolog</h3><p>Angular uses two ways to check component templates to be good:<br>1. Generating Type Checking Blocks — you can read more about it in <a href="https://medium.com/u/d59a9e801370">Alexey Zuev</a> article <a href="https://medium.com/angular-in-depth/type-checking-templates-in-angular-viewengine-and-ivy-77f8536359f5">here</a>.</p><p>2. Angular compiler instance uses TemplateParser which runs DomElementSchemaRegistry (implementation of <strong><em>ElementSchemaRegistry</em></strong>) methods for checking of template AST (<a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">Abstract syntax tree</a>) nodes validity.</p><p><em>ElementSchemaRegistry</em> is an abstract class that defines an interface for schema class for Angular to check its component templates (if there is such an element or if such attr exists for a specific element, etc). Here it is:</p><pre>import {SchemaMetadata, SecurityContext} from &#39;../core&#39;;</pre><pre>export abstract class ElementSchemaRegistry {</pre><pre>abstract <strong>hasProperty</strong>(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean;</pre><pre>abstract <strong>hasElement</strong>(tagName: string, schemaMetas: SchemaMetadata[]): boolean;</pre><pre>abstract securityContext(elementName: string, propName: string, isAttribute: boolean): SecurityContext;</pre><pre>abstract allKnownElementNames(): string[];</pre><pre>abstract getMappedPropName(propName: string): string;</pre><pre>abstract getDefaultComponentElementName(): string;</pre><pre>abstract validateProperty(name: string): {error: boolean, msg?: string};</pre><pre>abstract validateAttribute(name: string): {error: boolean, msg?: string};</pre><pre>abstract normalizeAnimationStyleProperty(propName: string): string;</pre><pre>abstract normalizeAnimationStyleValue(camelCaseProp: string, userProvidedProp: string, val: string|number): {error: string, value: string};</pre><pre>}</pre><p>As you can see that it has specific methods to check if some element or some attribute exists: <em>hasElement</em> and <em>hasAttribute</em>.</p><p>How does it work under-the-hood?</p><h3>DomElementSchemaRegistry extends ElementSchemaRegistry</h3><p>For browser elements checking we have a special class <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/schema/dom_element_schema_registry.ts">DomElementSchemaRegistry</a>. All DOM entities and its respective attributes are defined there. Here it is:</p><pre>const SCHEMA: string[] = [  &#39;[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot&#39; +      /* added manually to avoid breaking  changes */      <br>...<br>&#39;<strong>base</strong>^[HTMLElement]|href,target&#39;,  &#39;<strong>body</strong>^[HTMLElement]|aLink,background,bgColor,link,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,text,vLink&#39;,  &#39;button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value&#39;,  &#39;canvas^[HTMLElement]|#height,#width&#39;,  &#39;content^[HTMLElement]|select&#39;,  &#39;dl^[HTMLElement]|!compact&#39;,  &#39;datalist^[HTMLElement]|&#39;,  &#39;details^[HTMLElement]|!open&#39;,  &#39;dialog^[HTMLElement]|!open,returnValue&#39;,  &#39;dir^[HTMLElement]|!compact&#39;,  &#39;div^[HTMLElement]|align&#39;,  &#39;embed^[HTMLElement]|align,height,name,src,type,width&#39;,  &#39;fieldset^[HTMLElement]|!disabled,name&#39;,<br>...<br>  &#39;span^[HTMLElement]|&#39;,  &#39;<strong>style</strong>^[HTMLElement]|!disabled,media,type&#39;,  &#39;caption^[HTMLElement]|align&#39;,  &#39;th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width&#39;,  &#39;col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width&#39;,  &#39;<strong>table</strong>^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width&#39;,  &#39;<strong>tr</strong>^[HTMLElement]|align,bgColor,ch,chOff,vAlign&#39;,  &#39;tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign&#39;,  &#39;<strong>template</strong>^[HTMLElement]|&#39;,  &#39;<strong>textarea</strong>^[HTMLElement]|autocapitalize,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap&#39;,  &#39;title^[HTMLElement]|text&#39;,  &#39;track^[HTMLElement]|!default,kind,label,src,srclang&#39;,  &#39;ul^[HTMLElement]|!compact,type&#39;,  &#39;unknown^[HTMLElement]|&#39;,  &#39;video^media|#height,poster,#width&#39;,<br>...</pre><p>And the list is created by exporting browser IDL (interface description language) as we can see from <a href="https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli">compiler-cli</a>/<a href="https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src">src</a>/<a href="https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src/ngtsc">ngtsc</a>/<a href="https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src/ngtsc/typecheck">typecheck</a>/<a href="https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src/ngtsc/typecheck/src">src</a>/<a href="https://github.com/angular/angular/blob/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts#L63">dom.ts</a> file comments (one of the places where <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/schema/dom_element_schema_registry.ts">DomElementSchemaRegistry</a> is instantiated).</p><pre>`DomElementSchemaRegistry`, a schema * maintained by the Angular team via extraction from a browser IDL.</pre><p>OK, so we have a list of DOM entities and set of methods for checking whether an HTML template is valid. How does Angular use it?</p><h3>How is DomElementSchemaRegistry used?</h3><h4><strong>a) AotCompiler (</strong><a href="https://github.com/angular/angular/tree/8.2.x">angular</a>/<a href="https://github.com/angular/angular/tree/8.2.x/packages">packages</a>/<a href="https://github.com/angular/angular/tree/8.2.x/packages/platform-browser-dynamic">platform-browser-dynamic</a>/<a href="https://github.com/angular/angular/tree/8.2.x/packages/platform-browser-dynamic/src">src</a>/<a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler_factory.ts">compiler_factory.ts</a><strong>)</strong></h4><ol><li>Typescript <em>ts.createProgram</em> starter needs compiler instance from compiler factory function</li><li>compiler_factory (lets review AOT) creates <em>schemaParser</em> and feed it to <em>new TemplateParser(…)</em> statement and then templateParser instance are fed to compiler instance constructor (<a href="https://github.com/angular/angular/blob/f8096d499324cf0961f092944bbaedd05364eea1/packages/compiler/src/aot/compiler_factory.ts#L86-L88">here</a>)</li></ol><pre>export function <strong>createAotCompiler</strong>(</pre><pre>compilerHost: AotCompilerHost, options: AotCompilerOptions,</pre><pre>errorCollector?: (error: any, type?: any) =&gt;</pre><pre>void): {compiler: AotCompiler, reflector: StaticReflector} {</pre><pre>...</pre><pre>const <strong>elementSchemaRegistry</strong> = new DomElementSchemaRegistry();</pre><pre>const <strong>tmplParser</strong> = new TemplateParser(</pre><pre>config, staticReflector, expressionParser, <strong>elementSchemaRegistry</strong>, htmlParser, console, []);</pre><pre>...</pre><pre>const compiler = new <strong>AotCompiler</strong>(</pre><pre>config, options, compilerHost, staticReflector, resolver, <strong>tmplParser</strong>,</pre><pre>new StyleCompiler(urlResolver), viewCompiler, typeCheckCompiler,</pre><pre>new NgModuleCompiler(staticReflector),</pre><pre>new InjectableCompiler(staticReflector, !!options.enableIvy), new TypeScriptEmitter(),</pre><pre>summaryResolver, symbolResolver);</pre><pre>return {compiler, reflector: staticReflector};</pre><pre>}</pre><p>3. <br>a) Сompiler instance has two(actually more but we review these two) methods <strong><em>_compileComponent</em></strong> and <strong><em>_createTypeCheckBlock.</em></strong><br>b) These methods call <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler.ts#L647">_parseTemplate</a> where templateParserInstance.<a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/template_parser/template_parser.ts#L89">parse</a> is performed.</p><p>c) And it starts template parsing process (btw <a href="https://www.cse.wustl.edu/~cytron/cacweb/Tutorial/Visitor/">visitor pattern</a> is used to iterate over AST nodes) where our <em>DomElementSchemaRegistry</em> instance and TemplateParseVisitor instance are used to check whether AST node (which represents template element) is valid.</p><p>For example <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/template_parser/template_parser.ts#L687">here</a> and <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/template_parser/template_parser.ts#L743">here</a> you can see code that represents messages at the beginning of the article:</p><pre>private _assertElementExists(matchElement: boolean, element: html.Element) {</pre><pre>const elName = element.name.replace(/^:xhtml:/, &#39;&#39;);</pre><pre>if (!matchElement &amp;&amp; !this._schemaRegistry.hasElement(elName, this._schemas)) {</pre><pre>let errorMsg = `&#39;${elName}&#39; is not a known element:\n`;</pre><pre>errorMsg +=</pre><pre>`1. If &#39;${elName}&#39; is an Angular component, then verify that it is part of this module.\n`;</pre><pre>if (elName.indexOf(&#39;-&#39;) &gt; -1) {</pre><pre>errorMsg +=</pre><pre>`2. If &#39;${elName}&#39; is a Web Component then add &#39;CUSTOM_ELEMENTS_SCHEMA&#39; to the &#39;@NgModule.schemas&#39; of this component to suppress this message.`;</pre><pre>} else {</pre><pre>errorMsg +=</pre><pre>`2. To allow any element add &#39;NO_ERRORS_SCHEMA&#39; to the &#39;@NgModule.schemas&#39; of this component.`;</pre><pre>}</pre><pre>this._reportError(errorMsg, element.sourceSpan !);</pre><pre>}</pre><h4><strong>b) JIT compiler (</strong><a href="https://github.com/angular/angular/tree/8.2.x">angular</a>/<a href="https://github.com/angular/angular/tree/8.2.x/packages">packages</a>/<a href="https://github.com/angular/angular/tree/8.2.x/packages/platform-browser-dynamic">platform-browser-dynamic</a>/<a href="https://github.com/angular/angular/tree/8.2.x/packages/platform-browser-dynamic/src">src</a>/<a href="https://github.com/angular/angular/blob/8.2.x/packages/platform-browser-dynamic/src/compiler_factory.ts">compiler_factory.ts</a><strong>)</strong></h4><p>AOT compiler creates an instance for TemplateParser <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler_factory.ts#L87">explicitly</a> (you remember that parser then uses schemaRegistry (instance of DomElementSchemaRegistry)).</p><pre>//angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler_factory.ts</pre><pre>...<br><strong>const elementSchemaRegistry = new DomElementSchemaRegistry();</strong></pre><pre>const tmplParser = new TemplateParser(config, staticReflector, expressionParser, elementSchemaRegistry, htmlParser, console, []);<br>...</pre><p>For instantiating JIT Compiler Angular uses Injector to create respective dependencies instances to its <a href="https://github.com/angular/angular/blob/8.2.x/packages/platform-browser-dynamic/src/compiler_factory.ts#L162">compiler_factory</a>.</p><pre>// angular/angular/blob/8.2.x/packages/platform-browser-dynamic/src/compiler_factory.ts</pre><pre>export const COMPILER_PROVIDERS = &lt;StaticProvider[]&gt;[<br>...<br>{ provide: DomElementSchemaRegistry, deps: []},  <br>{ provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},<br>...<br>]</pre><p>This means that for JIT build we can substitute DomElementSchemaRegistry with some custom ElementSchema class (we will talk about it later).</p><p>Now we know how it works. And? What to do with this knowledge?</p><h3>Use cases</h3><p>Let’s review a few cases where <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/schema/dom_element_schema_registry.ts">DomElementSchemaRegistry</a> is used.</p><p><strong>#1 CUSTOM_ERRORS_SCHEMA when we have custom elements in Angular application.</strong></p><p>If you ever tried to use custom elements in Angular (you can read about it <a href="https://alligator.io/angular/using-custom-elements/">here</a>) — you already know that to prevent template errors we need to add CUSTOM_ERRORS_SCHEMA to <em>schemas:[]</em> property of NgModule decorator object:</p><pre>import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from &#39;@angular/core</pre><pre>...<br>@NgModule({</pre><pre>declarations: [AppComponent],</pre><pre>schemas: [ CUSTOM_ELEMENTS_SCHEMA ],</pre><pre>...</pre><pre>})</pre><pre>export class AppModule {}</pre><p>Why? Because Angular doesn&#39;t know anything about custom elements tags and treats them as some error in a template. You may think — how does adding CUSTOM_ELEMENTS_SCHEMA prevent Angular from throwing an error?</p><p>You remember that Template parser uses <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/schema/dom_element_schema_registry.ts">DomElementSchemaRegistry</a> instance (_schemaRegistry) to check for the template node&#39;s validity.</p><p>_<strong><em>schemaRegistry</em></strong> has two methods <em>_schemaRegistry.hasElement</em> and _schemaRegistry. <em>hasProperty. </em>Let&#39;s take a look at them in compiler/src/schema/<a href="https://github.com/angular/angular/blob/master/packages/compiler/src/schema/dom_element_schema_registry.ts#L285">dom_element_schema_registry.ts</a> file:</p><pre><strong>hasProperty</strong>(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean {</pre><pre>  if (schemaMetas.some((schema) =&gt; schema.name === NO_ERRORS_SCHEMA.name)) { return true; }</pre><pre><strong>  if (tagName.indexOf(&#39;-&#39;) &gt; -1) {</strong></pre><pre><strong>    if (isNgContainer(tagName) || isNgContent(tagName)) {</strong></pre><pre><strong>    return false;</strong></pre><pre><strong>    }</strong></pre><pre><strong>    if (schemaMetas.some((schema) =&gt; schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) { return true;}</strong></pre><pre> <strong> }</strong><br>...</pre><pre>}</pre><pre><strong>hasElement</strong>(tagName: string, schemaMetas: SchemaMetadata[]): boolean {</pre><pre>  if (schemaMetas.some((schema) =&gt; schema.name === NO_ERRORS_SCHEMA.name)) { return true; }</pre><pre> <strong> if (tagName.indexOf(&#39;-&#39;) &gt; -1) {</strong></pre><pre><strong>    if (isNgContainer(tagName) || isNgContent(tagName)) { return true; }</strong></pre><pre><strong>  if (schemaMetas.some((schema) =&gt; schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) { return true; }</strong></pre><pre>}<br>...</pre><pre>}</pre><p>Pay attention to that part:</p><pre><strong>if (tagName.indexOf(&#39;-&#39;) &gt; -1) {</strong></pre><pre><strong>if (isNgContainer(tagName) || isNgContent(tagName)) {</strong></pre><pre><strong>return false;</strong></pre><pre><strong>}</strong></pre><pre><strong>if (schemaMetas.some((schema) =&gt; schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) { return true;}</strong></pre><pre><strong>}</strong></pre><p>This means that if template tag has dash (-) in it, this tag is not <em>ng-content</em> and not <em>ng-container</em>, and <em>CUSTOM_ELEMENTS_SCHEMA</em> was added to ngModule schemas (schemaMetas variable) — then treat it as valid.</p><p>In other words, Angular just searches for a dash in name and if yes — treat it as custom element (so simple logic).</p><p>You can try the code in 2_CUSTOM_ELEMENTS_SCHEMA branch of the <a href="https://github.com/kievsash/ElementSchemaRegistryDemo/tree/2_CUSTOM_ELEMENTS_SCHEMA">article demo project</a>.</p><p><strong>#2 Using NO_ERRORS_SCHEMA for unit testing</strong></p><p>Looking at Angular code above you can also understand how <em>NO_ERRORS_SCHEMA</em> works:</p><pre>if (schemaMetas.some((schema) =&gt; schema.name === NO_ERRORS_SCHEMA.name)) { return true; }</pre><p>If we added <em>NO_ERRORS_SCHEMA </em>to <em>ngModule</em> decorator <em>schemas</em> prop — Angular doesn&#39;t do any checks at all.</p><p>It may be useful if you test your component but doesn&#39;t want to include child component into <em>TestBed</em> module configuration.</p><pre>// my-parent.component.ts</pre><pre>@Component({<br>  selector: &#39;app-my-parent&#39;,<br>  <strong>template</strong>: `<br>    &lt;div&gt;<br>      <strong>&lt;child&gt;&lt;/child&gt; &lt;!-- Don&#39;t want to instantiate child --&gt;<br>    </strong>&lt;/div&gt;<br>    &lt;button (click)=&quot;doSomething()&quot;&gt;Start&lt;/button&gt;<br>  `,<br>  styleUrls: [&#39;./my-parent.component.scss&#39;]<br>})</pre><pre>export class MyParentComponent {</pre><pre>  constructor() { }</pre><pre>  doSomething() {<br>    // some code<br>  }<br>}</pre><pre><br>// my-parent.spec.ts<br>...</pre><pre>beforeEach(async(() =&gt; {</pre><pre>  TestBed.configureTestingModule({</pre><pre>    declarations: [ MyParentComponent ],</pre><pre>    schemas: [ NO_ERRORS_SCHEMA ]</pre><pre>  }).compileComponents();</pre><pre>}));</pre><p>Without NO_ERRORS_SCHEMA Angular will throw that it doesn&#39;t know anything about &lt;child&gt;&lt;/child&gt;. But if you don&#39;t want to test &lt;child&gt; — then now you know how to omit it:-)</p><p>BTW someone <a href="https://medium.com/@fivedicephoto/why-you-shouldnt-use-no-errors-schema-in-angular-unit-tests-cdd478c30782">treats it as a bad practice</a> but it&#39;s up to you to decide.</p><p>You can check the code <a href="https://github.com/kievsash/ElementSchemaRegistryDemo/tree/3_NO_ERRORS_SCHEMA">here</a> (3_NO_ERRORS_SCHEMA branch).</p><p>Like this article? <a href="https://buymeacoffee.com/nqypxi6"><strong>Buy me a coffee</strong></a> :-)</p><h4>#3. Redefine ElementSchemaRegistry with a custom class (where some exclusion rules are defined) — JIT Compiler version.</h4><p>It may be needed if you use not only Angular on your web site so some custom elements (or attributes) should be added to exclusions.<br>I found a nice example of this case <a href="https://stackoverflow.com/questions/42953118/add-custom-elements-and-attributes-to-compiler-schema">in StackOverflow issue</a>:</p><blockquote>There are some custom elements and attributes in component template (in this example they are used by third-party non-Angular code):</blockquote><blockquote><em>&lt;foo&gt;&lt;/foo&gt;<br>&lt;div data-bar=&quot;{{ bar }}&quot;&gt;&lt;/div&gt;</em></blockquote><blockquote>They cause a compiler error:</blockquote><blockquote><em>Template parse errors:<br>&#39;foo&#39; is not a known element:</em></blockquote><blockquote>How can <em>foo</em> element and <em>data-bar</em> attribute be added to compiler schema?</blockquote><p>And the perfect answer from <a href="https://medium.com/u/d59a9e801370">Alexey Zuev</a>:</p><p><em>You can try to override </em><em>DomElementSchemaRegistry like this:</em></p><pre>//main.ts</pre><pre>import { DomElementSchemaRegistry, ElementSchemaRegistry } from &#39;@angular/compiler&#39;<br>import { SchemaMetadata } from &#39;@angular/core&#39;;</pre><pre>const MY_DOM_ELEMENT_SCHEMA = [<br>  &#39;foo&#39;<br>];</pre><pre>const MY_CUSTOM_PROPERTIES_SCHEMA = {<br>  &#39;div&#39;: {<br>    &#39;bar&#39;: &#39;string&#39;<br>  }<br>};</pre><pre>export class CustomDomElementSchemaRegistry extends DomElementSchemaRegistry {<br>  constructor() {<br>    super();<br>  }</pre><pre>hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean {<br>    return MY_DOM_ELEMENT_SCHEMA.indexOf(tagName) &gt; -1 || <br>         super.hasElement(tagName, schemaMetas);<br>  }</pre><pre>hasProperty(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean {<br>    const elementProperties = MY_CUSTOM_PROPERTIES_SCHEMA[tagName.toLowerCase()];<br>    return (elementProperties &amp;&amp; elementProperties[propName]) || <br>        super.hasProperty(tagName, propName, schemaMetas);<br>  }<br>}</pre><pre>platformBrowserDynamic().bootstrapModule(AppModule, {<br>  providers: [{ provide: ElementSchemaRegistry, useClass: CustomDomElementSchemaRegistry, deps: [] }]<br>});</pre><p>Now Angular doesn&#39;t show template error for &lt;foo&gt; element.</p><p>But this example works in JIT compilation only — check the code at <em>4_Customizing_element_schema</em> branch of <a href="https://github.com/kievsash/ElementSchemaRegistryDemo/tree/4_Customizing_element_schema">article demo project</a>.</p><p>Can we somehow make it work in AOT build?</p><h4>#4 Redefine ElementSchemaRegistry with a custom class (where some exclusion rules are defined) — AOT Compiler version.</h4><p>You remember that JIT Compiler uses <a href="https://github.com/angular/angular/blob/8.2.x/packages/platform-browser-dynamic/src/compiler_factory.ts#L92">COMPILER_PROVIDERS</a> (<a href="https://github.com/angular/angular/blob/8.2.x/packages/platform-browser-dynamic/src/compiler_factory.ts#L152">platform-browser-dynamic/src/compiler_factory.ts#L152</a>):</p><pre>{ provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},</pre><p>So it was easy to substitute ElementSchemaRegistrywith some other custom class (like we did in previous example).</p><p>But it is not possible to do so for AOT Compiler since in AOT compiler factory schemaRegistry is instantiated explicitly (<a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler_factory.ts#L86">aot/compiler_factory.ts#L86</a>):</p><pre>const elementSchemaRegistry = new DomElementSchemaRegistry();</pre><p>To make previous example work in AOT build we have to somehow re-defined DomElementSchemaRegistry.hasProperty (or hasElement) methods (thanks to <a href="https://medium.com/u/d59a9e801370">Alexey Zuev</a> for <a href="https://twitter.com/yurzui/status/1224676392744431616">providing</a> such an approach).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/662/1*L7mt4eUwyAWPMl6Q4fNpEQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-kpCVLHNFrju6tGcGOgLmw.png" /><figcaption>ng-new.js — Redefining DomElementSchemaRegistry.hasElement</figcaption></figure><p>How does it work?</p><p>We copy original <em>DomElementSchemaRegistry.hasElement </em>implementation and then expand it with esclusion code (but after that call original method as well)</p><p>To check if it works — clome <a href="https://github.com/kievsash/ElementSchemaRegistryDemo/tree/4_Customizing_element_schema">this repo</a> (branch: <em>4_Customizing_element_schema</em>) and run <strong>node ng serve --aot</strong> or <strong>node ng build --aot</strong>. Pay attention that <strong><em>ng </em></strong>in that command is not Angular CLI binary <em>ng</em> but our <em>ng.js</em> file.</p><p>I implemented the code in ng-new.js file.</p><p>Just clone the project, switch to branch and run: <strong>node ng-new build --prod </strong>. No errors! (<em>if you try</em> <strong><em>ng build — prod </em></strong><em>(standard Angular CLI command) — build will fail with template errors</em>).</p><h3>Imperfections</h3><p>What I noticed that even if we add an exclusion for custom attributes, like this:</p><pre>&lt;div bar<strong>=&quot;</strong>{{title}}<strong>&quot;</strong>&gt;<strong>Angular removed bar attribute from that div silently</strong>&lt;/div&gt;</pre><p>Angular will remove <em>bar</em> attribute 🤔.</p><p>If we want to keep it — you have to use Angular attr notation (but in this case no need to create exclusion rule)</p><pre>&lt;div [attr.bar]<strong>=&quot;</strong>title<strong>&quot;</strong>&gt;<strong>Angular keep bar attribute if we use [attr.bar]=&quot;&quot; notation</strong>&lt;/div&gt;</pre><p>For custom attributes the only case that works with providing schema exclusion is non-dynamic attributes:</p><pre>&lt;div bar<strong>=&quot;NonDymanicValue&quot;</strong>&gt;<strong>Angular removed bar attribute from that div silently</strong>&lt;/div&gt;</pre><h3>Any projects where customized ElementSchema is used?</h3><p>I know two projects where customized elementSchemaRegistry is used</p><h4><a href="https://github.com/Tibing/platform-terminal">#1 Angular Terminal Platform</a></h4><p>How to run an Angular application in a terminal window? Easy)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*TvXMVoVV24qVB6ZD2q4p1w.gif" /></figure><p>And here is a link to <a href="https://github.com/Tibing/platform-terminal/blob/master/projects/platform-terminal/src/lib/schema-registry.ts">schema-registry.ts</a> used in a project. Nothing special — we just validate everything:</p><pre>// <a href="https://github.com/Tibing/platform-terminal/blob/2392575bb0e289ccdeb460481f1cbaa6e7d1b664/projects/platform-terminal/src/lib/schema-registry.ts">projects/platform-terminal/src/lib/schema-registry.ts</a></pre><pre>export class TerminalElementSchemaRegistry extends ElementSchemaRegistry {</pre><pre>hasProperty(_tagName: string, _propName: string): boolean {<br>  return true;<br>}</pre><pre>hasElement(_tagName: string, _schemaMetas: SchemaMetadata[]): boolean {<br>return true;<br>}<br>...</pre><pre>// <a href="https://github.com/Tibing/platform-terminal/blob/2392575bb0e289ccdeb460481f1cbaa6e7d1b664/projects/platform-terminal/src/lib/platform.ts">projects/platform-terminal/src/lib/platform.ts</a><br>...</pre><pre>{ provide: ElementSchemaRegistry, useClass: TerminalElementSchemaRegistry, deps: [] },</pre><p>You already know what this means, right?😎</p><h4><a href="https://github.com/irustm/angular-nodegui">#2 Applications Node-GUI</a></h4><p>A project that allows running Angular applications as desktop applications (like Electron app.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*V72EidM4qTc1_O7s99dj-Q.png" /></figure><p>The author uses <a href="https://github.com/irustm/angular-nodegui/blob/master/projects/angular-nodegui/src/lib/schema-registry.ts">NodeguiElementSchemaRegistry</a> that also allows everything (returns <em>true</em> on any <em>hasElement</em> and <em>hasProperty</em> request). And like in Angular terminal project — we re-define ElementSchemaRegistry in <a href="https://github.com/irustm/angular-nodegui/blob/7712f4cb8653031908c2049a9bc99eedad9a5be1/projects/angular-nodegui/src/lib/platform-dynamic.ts#L21">COMPILER_OPTIONS</a> in <a href="https://github.com/irustm/angular-nodegui/tree/7712f4cb8653031908c2049a9bc99eedad9a5be1/projects/angular-nodegui">angular-nodegui</a>/<a href="https://github.com/irustm/angular-nodegui/tree/7712f4cb8653031908c2049a9bc99eedad9a5be1/projects/angular-nodegui/src">src</a>/<a href="https://github.com/irustm/angular-nodegui/tree/7712f4cb8653031908c2049a9bc99eedad9a5be1/projects/angular-nodegui/src/lib">lib</a>/<a href="https://github.com/irustm/angular-nodegui/blob/7712f4cb8653031908c2049a9bc99eedad9a5be1/projects/angular-nodegui/src/lib/platform-dynamic.ts#L25">platform-dynamic.ts</a> file:</p><pre>provide: COMPILER_OPTIONS,<br>  useValue: {<br>    providers: [{<br>     provide: ElementSchemaRegistry,<br>     useClass: NodeguiElementSchemaRegistry,<br>     deps: []<br>   }<br>...</pre><h3>Conclusion</h3><p>So let&#39;s wrap up:</p><ol><li>Angular uses ElementSchemaRegistry and its implementation — DomElementSchemaRegistry — to check if attributes and elements in the component&#39;s template are valid</li><li>An instance of DomElementSchemaRegistry is used in TemplateParser which is used in the compiler to visit all template AST Nodes (JIT and AOT).</li><li>We can create our custom rules for validation. It is easy for JIT (platformBrowserDynamic) compiler and a bit tricky but also possibly for AOT build.</li><li>We can switch off validation by proving CUSTOM_ELEMENTS_SCHEMA and NO_ERRORS_SCHEMA in ngModule config object <em>schemas</em> property.</li><li>You can find all the code I used in the article <a href="https://github.com/kievsash/ElementSchemaRegistryDemo">here</a></li></ol><p>Did you like this article? Then clap twice 🤓</p><h3>More to read:</h3><ol><li>T<a href="https://medium.com/angular-in-depth/type-checking-templates-in-angular-viewengine-and-ivy-77f8536359f5">ype-checking templates in Angular ViewEngine and Ivy</a> by <a href="https://medium.com/u/d59a9e801370">Alexey Zuev</a>.</li><li>How Angular builds and run — very good explanatory answer on StackOverflow — <a href="https://stackoverflow.com/questions/48942691/how-angular-builds-and-runs">here</a>.</li></ol><p>3. <a href="https://medium.com/angular-in-depth/a-deep-deep-deep-deep-deep-dive-into-the-angular-compiler-5379171ffb7a">Deep, deep, deep dive into Angular compiler</a> by <a href="https://medium.com/u/355b1dfe86ae">Uri Shaked</a>.</p><p>4. MockSchemaRegistry example in Angular source code: <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/test/template_parser/template_parser_spec.ts#L29">here</a> and <a href="https://github.com/angular/angular/blob/8.2.x/packages/compiler/testing/src/schema_registry_mock.ts#L11">here</a>.</p><p>5. <a href="https://alligator.io/angular/using-custom-elements/">Using Custom Elements in Angular</a>.</p><p>Let’s keep in touch on <a href="https://twitter.com/El_Extremal"><strong>Twitter</strong></a>!</p><ul><li>BTW. I started a video tutorial series “<a href="https://www.youtube.com/watch?v=HI5f6naJdJ8&amp;list=PLNadw4d8-KMVSOffiYBuOlzvF38sO9pdu&amp;utm_content=educational&amp;utm_campaign=2019-11-13&amp;utm_source=email-sendgrid&amp;utm_term=8565702&amp;utm_medium=2586716"><strong>Angular can waste your time</strong></a>” on Youtube. There I will be publishing videos about resolving tricky cases with Angular and RxJS. <a href="https://www.youtube.com/watch?v=HI5f6naJdJ8&amp;list=PLNadw4d8-KMVSOffiYBuOlzvF38sO9pdu&amp;utm_content=educational&amp;utm_campaign=2019-11-13&amp;utm_source=email-sendgrid&amp;utm_term=8565702&amp;utm_medium=2586716"><strong>Take a look!</strong></a></li></ul><figure><a href="https://ukr.us20.list-manage.com/subscribe?u=51363eb47063b2905f3ba0cf7&amp;id=d58900bdac"><img alt="" src="https://cdn-images-1.medium.com/max/594/1*a601SFy8KekkveKtqNXbrw.png" /></a></figure><p><strong>Special thanks to </strong><a href="https://medium.com/u/d59a9e801370"><strong>Alexey Zuev</strong></a><strong>, </strong><a href="https://medium.com/u/d8bcb48503e4"><strong>Michael Karén</strong></a><strong> and </strong><a href="https://medium.com/u/f0e7507974eb"><strong>Lars Gyrup Brink Nielsen</strong></a><strong> for helping with and reviewing the article!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=83d54cd31478" width="1" height="1" alt=""><hr><p><a href="https://medium.com/angular-in-depth/angular-elementschemaregistry-for-dummies-83d54cd31478">Angular ElementSchemaRegistry for “dummies”</a> was originally published in <a href="https://medium.com/angular-in-depth">Angular In Depth</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RxJS in-depth: Github repo ‘utils’ directory review (part 1)]]></title>
            <link>https://medium.com/angular-in-depth/rxjs-in-depth-github-repo-utils-directory-review-part-1-d0aa15adaeb9?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/d0aa15adaeb9</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[rxjs]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Wed, 22 Jan 2020 09:37:00 GMT</pubDate>
            <atom:updated>2020-04-29T18:02:05.928Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Curiosity leads to discoveries.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jn-W-n6AlyguqRtMCCUWUw.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/743/0*jCeuuPhBt4j8uwOn.png" /></figure><blockquote><a href="https://medium.com/angular-in-depth"><strong><em>AngularInDepth</em></strong></a><strong><em> is moving away from Medium. More recent articles are hosted on the new platform </em></strong><a href="https://indepth.dev/angular/"><strong><em>inDepth.dev</em></strong></a><strong><em>. Thanks for being part of indepth movement!</em></strong></blockquote><p><em>RxJS is quite a popular library in Angular world so for me it is always interesting to find out something new about its internals. Today I want to dig into </em><a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src/internal/util"><strong><em>utils</em></strong></a><strong><em> </em></strong><em>folder of its GitHub </em><a href="https://github.com/ReactiveX/rxjs"><em>repo</em></a><em>. This article is written not for some practical usage but rather for reverse engineering enthusiasts and my curiosity.</em></p><p>Here is a link to RxJS 6.5 /<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src">src</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src/internal">internal</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src/internal/util">util</a>/ dir. This folder is full of files. Let’s review them one by one. Some of them can bring something interesting 🔍.</p><h4>#1 <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/util/ArgumentOutOfRangeError.ts">ArgumentOutOfRangeError.ts</a></h4><p>The main code is next:</p><pre>function <strong>ArgumentOutOfRangeErrorImpl</strong>(this: any) {  <br> Error.call(this);  <br> this.message = &#39;argument out of range&#39;;  <br> this.name = &#39;ArgumentOutOfRangeError&#39;;  <br> return this;<br>}</pre><pre><em>ArgumentOutOfRangeErrorImpl</em>.prototype = Object.<em>create</em>(Error.prototype);</pre><pre><em>export const </em><strong>ArgumentOutOfRangeError</strong>: ArgumentOutOfRangeErrorCtor = <em>ArgumentOutOfRangeErrorImpl as any</em>;</pre><p><strong>What is interesting here?</strong></p><p>a) We inherit from built-in Error class here. So we can <em>throw</em> calls instance with the message we need.</p><p>b) superclass constructor is called in an explicit way: Error.call(this);</p><p><strong>Where it is used in RxJS source code?</strong></p><p>Let&#39;s take a look at <a href="https://rxjs-dev.firebaseapp.com/api/operators/takeLast"><strong><em>takeLast</em></strong></a><strong><em>(count)</em></strong> operator — it should emit last <em>count </em>values after the source observable is complete. Of cause it cannot accept negative count — <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/operators/takeLast.ts#L40">so</a>:</p><pre>* @throws {<strong>ArgumentOutOfRangeError</strong>} When using `takeLast(i)`, it delivers an * ArgumentOutOrRangeError to the Observer&#39;s `error` callback if `i &lt; 0`</pre><p>at <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/operators/takeLast.ts#L63">line 63</a>:</p><pre>if (this.total &lt; 0) {      throw new ArgumentOutOfRangeError;    }</pre><p><strong>Why should I know this?</strong></p><p>Reading source code can help you to understand how the library works in edge cases. And sometimes it is the only way to understand why it works how it works.</p><h4>#2 <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/util/EmptyError.ts">EmptyError.ts</a></h4><p>It looks very similar to ArgumentOutOfRangeError</p><pre>function EmptyErrorImpl(this: any) {<br>  Error.call(this);<br>  this.message = &#39;no elements in sequence&#39;;<br>  this.name = &#39;EmptyError&#39;;<br>  return this;<br>}<br><br>EmptyErrorImpl.prototype = Object.create(Error.prototype);<br><br>export const EmptyError: EmptyErrorCtor = EmptyErrorImpl as any;</pre><p>But the application scope is different.</p><p><strong>Where it is used in code?</strong></p><p>Let&#39;s review <a href="https://rxjs-dev.firebaseapp.com/api/operators/first"><strong><em>first</em></strong></a> operator — it should emit only the first value (or the first value that meets some condition) emitted by the source Observable.</p><p>But what is sequence is empty? We can find an answer at <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/operators/first.ts#L69">line 69</a> of <a href="https://github.com/ReactiveX/rxjs/tree/6.5.1">rxjs</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src">src</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src/internal">internal</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src/internal/operators">operators</a>/<a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/operators/first.ts">first.ts</a></p><pre><em>* </em><strong><em>@throws </em></strong><em>{</em><strong><em>EmptyError</em></strong><em>} Delivers an EmptyError to the Observer&#39;s `error`<br>* callback if the Observable completes before any `next` notification was sent.<br>*</em></pre><p>And this happens in lines 86–90:</p><pre>return (source: Observable&lt;T&gt;) =&gt; source.pipe(<br>    predicate ? filter((v, i) =&gt; predicate(v, i, source)) : identity,<br>    take(1),<br>    hasDefaultValue ? defaultIfEmpty&lt;T | D&gt;(defaultValue) : throwIfEmpty(() =&gt; new <strong>EmptyError</strong>()),<br>  );</pre><p><strong>*Interesting remark:</strong> you can use <strong>take(1) </strong>instead of<strong> first() </strong>with almost the same result:</p><pre>source$.pipe(take(1))<br>vs<br>source$.pipe(first())</pre><p>The only difference that <strong><em>take</em></strong> operator will not emit <strong>EmptyError </strong>if source$ completes before producing value.</p><p><strong>Why should I know this?</strong><br>Since <strong>first</strong> operator uses <strong>EmptyError</strong> — a final code bundle should contain <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/util/EmptyError.ts"><strong>EmptyError.ts</strong></a> file too. So when you use <strong>first</strong> operator — a final bundle size will be a bit bigger. You can read more about it <a href="https://swalsh.org/blog/rxjs-first-vs-take1/">here</a>.</p><h4>#3 <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/util/Immediate.ts">Immediate.ts</a></h4><pre>export const <strong>Immediate</strong> = {<br>  <strong>setImmediate</strong>(cb: () =&gt; void): number {<br>    const handle = nextHandle++;<br>    tasksByHandle[handle] = cb;<br>    <strong>Promise.resolve().then(() =&gt; runIfPresent(handle));</strong><br>    return handle;<br>  },<br><br>  <strong>clearImmediate</strong>(handle: number): void {<br>    delete tasksByHandle[handle];<br>  },<br>};</pre><p>Just a wrapper for putting<strong> </strong>tasks to microtasks queue. it is used in <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/scheduler/AsapAction.ts">AsapAction</a> which is used by RxJS <a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/scheduler/AsapScheduler.ts">AsapScheduler</a>. <em>Schedulers</em> is a separate big topic, you can read more about it in my article <a href="https://blog.cloudboost.io/so-how-does-rx-js-queuescheduler-actually-work-188c1b46526e">here</a>. But just for this case — it schedules data to be emitted in a m<strong><em>i</em></strong>crotask just after the current m<strong><em>a</em></strong>crotask in browser EvenLoop queue.</p><p>It is done by this code:</p><pre><strong>Promise.resolve().then(() =&gt; runIfPresent(handle));</strong></pre><p>Here is a usage example in <a href="https://github.com/ReactiveX/rxjs/tree/6.5.1">rxjs</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src">src</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src/internal">internal</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.5.1/src/internal/scheduler">scheduler</a>/<a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/scheduler/AsapAction.ts#L27">AsapAction.ts</a>:</p><pre>return scheduler.scheduled || (scheduler.scheduled = <strong>Immediate.setImmediate</strong>(<br>      scheduler.flush.bind(scheduler, null)<br>    ));</pre><p><strong>Why should I know this?<br></strong>Knowing how RxJS schedule tasks (by means of special entity — Scheduler) is the key part of understanding how to write unit tests for RxJS code in Angular applications.</p><p><strong>I released FREE video-course about main unit testing tools for RxJS code. You can get it </strong><a href="https://mailchi.mp/9c8299ec62c2/rxjsunittesting"><strong>here!</strong></a></p><p>If you are curious about Schedulers topic in RxJS take a look at:</p><ol><li>“<a href="https://blog.cloudboost.io/so-how-does-rx-js-queuescheduler-actually-work-188c1b46526e">So how does RxJS QueueScheduler actually work?</a>” which also contains also common Schedulers intro.</li><li>“<a href="https://blog.angularindepth.com/rxjs-applying-asyncscheduler-as-an-argument-vs-with-observeon-operator-2340af3b6cac">RxJS: applying asyncScheduler as an argument vs with observeOn operator</a>”</li><li>Videos by <a href="https://twitter.com/Michael_Hladky">Michael Hladky</a>:<br>“<a href="https://www.youtube.com/watch?v=S1eDh7MonbI">RxJS schedulers in depth</a>”<br>“<a href="https://www.youtube.com/watch?v=wfSKE7GtKhU">RxJS Schedulers from outer space</a>”</li></ol><p><em>Do you like the article? You can find more interesting RxJS staff in my video-course “</em><a href="https://www.udemy.com/hands-on-rxjs-for-web-development/"><em>Hands-on RxJS</em></a><em>”. It may be interesting as for beginners (Section 1–3) as well as experiences RxJS developers (Sections 4–7). Buy it, watch it and leave comments and evaluations!</em></p><p><a href="https://www.udemy.com/hands-on-rxjs-for-web-development/">Hands-On RxJS for Web Development | Udemy</a></p><p><strong>#4 </strong><a href="https://github.com/ReactiveX/rxjs/blob/6.5.1/src/internal/util/ObjectUnsubscribedError.ts"><strong>ObjectUnsubscribedError.ts</strong></a></p><pre>function ObjectUnsubscribedErrorImpl(<em>this</em>: any) {<br>  <em>Error</em>.call(this);<br>  this.message = &#39;object unsubscribed&#39;;<br>  this.name = &#39;ObjectUnsubscribedError&#39;;<br>  return this;<br>}<br><br>ObjectUnsubscribedErrorImpl.prototype = <em>Object</em>.create(<em>Error</em>.prototype);<br><br>...<br>export const <strong>ObjectUnsubscribedError</strong>: ObjectUnsubscribedErrorCtor = ObjectUnsubscribedErrorImpl as any;</pre><p>This error is thrown when someone tries to emit with closed Subject.</p><p><strong>Where is it used in RxJS source code?</strong></p><p>For example, in a <a href="https://github.com/ReactiveX/rxjs/tree/6.x">rxjs</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.x/src">src</a>/<a href="https://github.com/ReactiveX/rxjs/tree/6.x/src/internal">internal</a>/<a href="https://github.com/ReactiveX/rxjs/blob/6.x/src/internal/Subject.ts#L77">Subject.ts</a> we throw this error in case someone wants to emit using Subject which is forcefully unsubscribed all the observers by calling special <a href="https://github.com/ReactiveX/rxjs/blob/6.x/src/internal/Subject.ts#L105">Subject.unsubscribe</a> method.</p><pre>const subj = new Subject();<br>let subsciption = subj.subscribe(console.log);<br>subj.next(&#39;Before force unsubscription&#39;);<br>subj.unsubscribe();<br>subj.next(&#39;After force unsubscription&#39;); // Throw here!</pre><p>You can check this behavior in a <a href="https://codepen.io/kievsash/pen/OJPrLLa?editors=0010">codepen playground</a>.</p><p>I asked <a href="https://medium.com/u/d05557088657">Nicholas Jamieson</a> (RxJS core team) for possible use-cases of <em>Subject.unsubscribe</em> method and he did a favor to answer me:</p><blockquote>There isn’t a usecase that I would consider valid. IMO, it should be removed. All it does is drop references to all subscribers and ensure it can’t emit further notifications. It just confuses people.<br>AFAICT, in .NET <em>dispose</em> is the terminology - instead of <em>unsubscribe</em> - and that makes a little more sense. With the method named <em>unsubscribe</em>, it&#39;s misleading.</blockquote><p><strong>#5 </strong><a href="https://github.com/ReactiveX/rxjs/blob/6.x/src/internal/util/TimeoutError.ts"><strong>TimeoutError.ts</strong></a></p><pre>function TimeoutErrorImpl(<em>this</em>: any) {<br>  <em>Error</em>.call(this);<br>  this.message = &#39;Timeout has occurred&#39;;<br>  this.name = &#39;TimeoutError&#39;;<br>  return this;<br>}<br><br>TimeoutErrorImpl.prototype = <em>Object</em>.create(<em>Error</em>.prototype);<br><br>...<br>export const TimeoutError: TimeoutErrorCtor = TimeoutErrorImpl as any;</pre><p>If we make a search on <em>TimeoutError </em>in <a href="https://github.com/ReactiveX/rxjs/search?q=TimeoutError&amp;unscoped_q=TimeoutError">RxJS github repo</a> we can find out that it is used for <a href="https://rxjs.dev/api/operators/timeout"><strong>timeout</strong></a> operator implementation:</p><pre>export function <strong>timeout</strong>&lt;T&gt;(due: number | Date,                           scheduler: SchedulerLike = async): MonoTypeOperatorFunction&lt;T&gt; {       return <strong>timeoutWith</strong>(due, throwError(<strong>new TimeoutError()</strong>), scheduler);<br>}</pre><p>Pay attention to interesting <a href="https://github.com/ReactiveX/rxjs/blob/26e16d7e82379cd09fc87ed1315ebd36b82d6f33/src/internal/operators/timeoutWith.ts"><strong><em>timeoutWith</em></strong></a> operator. We can find a description of it in its source code:</p><blockquote>`timeoutWith` is a variation of `timeout` operator. It behaves exactly the same, still accepting as a first argument either a number or a Date, which control — respectively — when values of source Observable should be emitted or when it should complete.</blockquote><blockquote><strong>The only difference is that it accepts a second, required parameter. This parameter should be an Observable which will be subscribed when source Observable fails any timeout check.</strong></blockquote><p>So from that phrase, we can do two interesting conclusions:<br>1. throwError(<strong>new TimeoutError())</strong> is the initially erred Observable that will throw with <em>TimeoutError </em>if no source$ emissions happen during the specified timeout.</p><p>2. By using <a href="https://rxjs.dev/api/operators/timeoutWith"><strong><em>timeoutWith</em></strong></a> operator we can provide our own Observable that will be subscribed in case no source$ emissions happen.</p><p>Quite interesting!</p><h4><strong>Takeaways</strong></h4><p>Let&#39;s wrap up what we&#39;ve learned today:</p><ol><li><a href="https://rxjs-dev.firebaseapp.com/api/operators/takeLast"><strong><em>takeLast</em></strong></a><strong><em>(count) </em></strong>will throw on negative values.</li><li>Where is a diff between <strong>take(1)</strong> and <strong>first()</strong> operators: take(1) will produce a smaller bundle size.</li><li>RxJS uses <strong>Promise.resolve().then </strong>for so-called <em>Immediate</em> value emission (used by AsapScheduler).</li><li>You can drop all subscribers with Subject.unsubscribe method. And if after that you will try to emit — Subject will throw an error.</li><li>There is an interesting <a href="https://rxjs.dev/api/operators/timeoutWith"><strong><em>timeoutWith</em></strong></a> operator, that allows to provide observable which will be subscribed if source$ will not emit in specified timeout.</li></ol><p>I hope you enjoyed the article and that it will encourage you to do (and blog:) your own discoveries!</p><p>If you like it — plz clap. If you want part 2 — clap twice :-)</p><p>Let’s keep in touch on <a href="https://twitter.com/El_Extremal">Twitter</a>.</p><p>Cheers!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d0aa15adaeb9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/angular-in-depth/rxjs-in-depth-github-repo-utils-directory-review-part-1-d0aa15adaeb9">RxJS in-depth: Github repo ‘utils’ directory review (part 1)</a> was originally published in <a href="https://medium.com/angular-in-depth">Angular In Depth</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Angular Material matInput control with thousands separator]]></title>
            <link>https://medium.com/angular-in-depth/angular-material-matinput-control-with-thousands-separation-ebcbb7b027f4?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/ebcbb7b027f4</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[forms]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[angular-material]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Thu, 26 Dec 2019 13:30:28 GMT</pubDate>
            <atom:updated>2022-03-23T11:04:55.072Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Customizing an Angular Material directive by adding commas to digit groups in matInput numbers and support reactive Angular forms.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GK_2CbjqAlA-QA1lWLgRCg.jpeg" /><figcaption>commified numbers</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/743/0*ipeBUMeQs6riNU0f.png" /></figure><blockquote><a href="https://medium.com/angular-in-depth"><strong><em>AngularInDepth</em></strong></a><strong><em> is moving away from Medium. More recent articles are hosted on the new platform </em></strong><a href="https://indepth.dev/angular/"><strong><em>inDepth.dev</em></strong></a><strong><em>. Thanks for being part of indepth movement!</em></strong></blockquote><p>Every Angular developer knows Angular Material (You don&#39;t know it? Then definitely <a href="https://material.angular.io/">take a look</a>). Like many other UI libraries it provides a set of form controls and among them <a href="https://material.angular.io/components/input/overview">matInput</a>.</p><p>It is a regular text input with the possibility to display placeholder in a nice way, to add a <em>clear</em> button, provide custom error messages, text length hint, and even suffixes and prefixes to improve user experience. You can look at examples <a href="https://material.angular.io/components/input/examples">here</a>.</p><h3>The problem</h3><p>The task I got from a customer was to display input values in digit groups for account amount form field, like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/460/1*AMZOzQOT7qcozNNKpMq65A.png" /><figcaption>Commified input</figcaption></figure><p>Surprisingly searching though the internet didn&#39;t provide some solid solutions so I decided to create my own one. I found only two things that may help:</p><ul><li>This interesting paragraph is on the <a href="https://material.angular.io/components/input/api">matInput API page</a>:</li></ul><blockquote>MAT_INPUT_VALUE_ACCESSOR</blockquote><blockquote>This token is used to inject the object whose value should be set into MatInput. If none is provided, the native HTMLInputElement is used. Directives like MatDatepickerInput can provide themselves for this token, in order to make MatInput delegate the getting and setting of the value to them</blockquote><ul><li>Angular Material’s <a href="https://material.angular.io/components/datepicker/overview"><em>MatDatePicker</em></a> expects to be applied on top of <em>matInput </em>so we can use <a href="https://github.com/angular/components/blob/49a1324acc05cec1c5ff28d729abfe590f6772dd/src/material/datepicker/datepicker-input.ts#L294-L296">its code</a> as an example of extending <em>matInput</em>.</li></ul><h3>Solution</h3><p>What can we take from matDatePicker code (and not only) for our goals?:<br>1. It redefines MAT_INPUT_VALUE_ACCESSOR provider so we can display some text value in the DOM input field (since we need digit grouping), but return numeric value for <em>matInput</em> validation.</p><p>2. To apply our formatting on Angular FormControl operations (like set value to form field) we also have to implement the <em>ControlValueAccessor</em> interface (you can read more custom form controls in Angular <a href="https://alligator.io/angular/custom-form-control/">here</a>).</p><p>3. And a few more things we need to implement:</p><ul><li><em>onBlur</em> input field event should format value</li><li><em>onFocus</em> event should unformat the value so that we can edit it as a regular number</li><li>onInput event should send a numeric representation of value with _onChange handler (<em>ControlValueAccessor implementation</em>)</li><li>Since we’re going to display commas, the input type should be set as <em>text </em>(not a number) but we have to make validation work (for example, <em>min</em> and <em>max</em> value)</li></ul><p>Before we start, let&#39;s create the reactive Angular form where we are going to use our form control directive:</p><pre><strong>&lt;!-- app.component.html --&gt;</strong><br>&lt;form [formGroup]<strong>=&quot;</strong>myForm<strong>&quot;</strong>&gt;<br>  &lt;mat-form-field appearance<strong>=&quot;</strong>outline<strong>&quot;</strong>&gt;<br>    &lt;mat-label&gt;Deposit Amount&lt;/mat-label&gt;<br>    &lt;input matInput &lt;- Material input<br>           <strong>matInputCommified &lt;--our directive</strong><br>           formControlName<strong>=&quot;</strong>deposit<strong>&quot;<br>           </strong>type<strong>=&quot;</strong>text<strong>&quot;</strong>/&gt;<br>  &lt;/mat-form-field&gt;<br>&lt;/form&gt;<br>&lt;button [disabled]<strong>=&quot;</strong>myForm.invalid<strong>&quot;<br>        </strong>mat-raised-button<br>        color<strong>=&quot;</strong>primary<strong>&quot;<br>        </strong>(click)<strong>=&quot;</strong>onSubmit()<strong>&quot;<br></strong>&gt;<strong>Submit</strong>&lt;/button&gt;</pre><pre><strong>// app.component.ts</strong><br>export class AppComponent {<br>  title = &#39;ngx-material-tools-demo&#39;;<br><br>  constructor(private formBuilder: FormBuilder) {<br>  }<br><br>  myForm = this.formBuilder.group({<br>    deposit: [&#39;&#39;, [<br>      Validators.required, // Validators<br>      Validators.min(1),<br>      Validators.max(1000000)<br>    ]],<br>  });<br><br>  onSubmit() {<br>    <em>// some operations here</em><br>  }<br>}</pre><h4>Supporting the MAT_INPUT_ACCESSOR API</h4><p>If we take a look at MAT_INPUT_ACCESSOR, it requires a very simple interface:</p><pre>export declare const MAT_INPUT_VALUE_ACCESSOR: InjectionToken&lt;{<br>    value: any;<br>}&gt;;</pre><p>If we declare a MAT_INPUT_VALUE_ACCESSOR provider the <em>matInput</em> directive will start working with DOM input element through our directive. In fact, it will think that our directive instance is a DOM input element which has a <em>value </em>property.</p><p>Why do we need it? Because when we set a value we need to apply formatting with commas to separate digit groups. When we get the value we have to remove commas and return a number to the matInput instance to support Angular validators could work correctly.</p><p>So all we have to do is to provide <em>value </em>as<em> </em>both setter and getter. Let&#39;s do this:</p><pre><strong><br>//mat-input-commified.directive.ts<br></strong>import {Directive, ElementRef, forwardRef, HostListener, Input} from &#39;@angular/core&#39;;<br>import {MAT_INPUT_VALUE_ACCESSOR} from &#39;@angular/material&#39;;<br>import {NG_VALUE_ACCESSOR} from &#39;@angular/forms&#39;;<br>import {numberWithCommas} from &#39;./helpers&#39;;<br><br>@Directive({<br>  selector: &#39;input[matInputCommified]&#39;,<br>  providers: [<br>    {provide: <strong>MAT_INPUT_VALUE_ACCESSOR</strong>, useExisting: MatInputCommifiedDirective}<br>})<br>export class <strong>MatInputCommifiedDirective</strong> {<br><br>  private _value: string | null;<br><br>  constructor(private elementRef: ElementRef&lt;HTMLInputElement&gt;,<br>  ) {}<br><br><br>  <strong>get value()</strong>: string | null {<br>    return this._value;<br>  }<br><br>  <strong>@Input</strong>(&#39;value&#39;)<br>  <strong>set value(<em>value</em>: string | null)</strong> {<br>    this._value = <em>value</em>;<br>    this.formatValue(<em>value</em>);<br>  }<br><br>  private formatValue(<em>value</em>: string | null) {<br>    if (<em>value </em>!== null) { <br>      this.elementRef.nativeElement.value = numberWithCommas(value);<br>    } else {<br>      this.elementRef.nativeElement.value = &#39;&#39;;<br>    }<br>  }<br><br><br>}</pre><p>You may think that setter here works even if we assign <em>formControl</em> value but no:</p><pre>this.myForm.get(&#39;deposit&#39;).setValue(10)  // setter is not called</pre><pre>...</pre><pre>&lt;input matInput &lt;- Material input<br>           matInputCommified<br>           formControlName<strong>=&quot;</strong>deposit<strong>&quot; <br>           [value]=&quot;10&quot; // setter is called in that case          <br>           </strong>type<strong>=&quot;</strong>text<strong>&quot;</strong>/&gt;</pre><p>The setter is called only when we assign some value with [value] property binding for the <em>matInput</em> directive (you can check <a href="https://github.com/angular/components/blob/49a1324acc05cec1c5ff28d729abfe590f6772dd/src/material/input/input.ts#L200-L204">matInput source code here</a> and run reproduce <a href="https://ng-run.com/edit/tpcJCmhLJRUsx9L4X6QW">here</a> — thanks to <a href="https://medium.com/u/d59a9e801370">Alexey Zuev</a> for providing that info!).</p><p>To make formatting work even for <em>formControl.setValue</em> call we have to implement the CONTROL_VALUE_ACCESSOR interface (we will do that later).</p><h4>Adding onBlur, onFocus, and onInput</h4><p>OK, now we have to implement the remaining behavior:</p><ol><li>Format on input blur event (when we finish editing).</li><li>Unformat text on focus event (when we start editing we don&#39;t need commas).</li><li>On text typing (onInput event), we should take the input field value and save it to the internal this._value property without any non-number symbols (only 0–9, period and minus are allowed).</li></ol><p>Let&#39;s do that:</p><pre>import {Directive, ElementRef, forwardRef, HostListener, Input} from &#39;@angular/core&#39;;<br>import {MAT_INPUT_VALUE_ACCESSOR} from &#39;@angular/material&#39;;<br>import {NG_VALUE_ACCESSOR} from &#39;@angular/forms&#39;;<br>import {numberWithCommas} from &#39;./helpers&#39;;<br><br>@Directive({<br>  selector: &#39;input[matInputCommified]&#39;,<br>  providers: [<br>    {provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: MatInputCommifiedDirective}<br>  ]<br>})<br>export class MatInputCommifiedDirective {<br>  // tslint:disable-next-line:variable-name<br>  private _value: string | null;<br><br>  constructor(private elementRef: ElementRef&lt;HTMLInputElement&gt;,<br>  ) {<br>    <em>console</em>.log(&#39;created directive&#39;);<br>  }<br><br><br>  get value(): string | null {<br>    return this._value;<br>  }<br><br>  @Input(&#39;value&#39;)<br>  set value(<em>value</em>: string | null) {<br>    this._value = <em>value</em>;<br>    this.formatValue(<em>value</em>);<br>  }<br><br>  private formatValue(<em>value</em>: string | null) {<br>    if (<em>value </em>!== null) {<br>      this.elementRef.nativeElement.value = numberWithCommas(value);<br>    } else {<br>      this.elementRef.nativeElement.value = &#39;&#39;;<br>    }<br>  }<br><br>  private unFormatValue() {<br>    const value = this.elementRef.nativeElement.value;<br>    this._value = value.replace(/[^\d.-]/g, &#39;&#39;);<br>    if (value) {<br>      this.elementRef.nativeElement.value = this._value;<br>    } else {<br>      this.elementRef.nativeElement.value = &#39;&#39;;<br>    }<br>  }<br><br> <strong> @HostListener(&#39;input&#39;, [&#39;$event.target.value&#39;])</strong><br>  onInput(<em>value</em>) {<br>    <strong>// here we cut any non numerical symbols </strong>   <br>    this._value = <em>value</em>.replace(/[^\d.-]/g, &#39;&#39;);<br>  }<br><br>  <strong>@HostListener(&#39;blur&#39;)</strong><br>  _onBlur() {<br>    this.formatValue(this._value); // add commas<br>  }<br><br> <strong> @HostListener(&#39;focus&#39;)</strong><br>  onFocus() {<br>    this.unFormatValue(); // remove commas for editing purpose<br>  }<br><br>}</pre><p>Ok, let&#39;s check how it works now:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/315/1*COYGQelL9h4OZSJ0eHI46A.gif" /></figure><p>Nice!</p><p>Now let&#39;s implement the CONTROL_VALUE_ACCESSOR interface to format the value even if we assign it with the <em>FormControl#setValue</em> method.</p><h4>Adding CONTROL_VALUE_ACCESSOR</h4><p>In the <a href="https://angular.io/api/forms/ControlValueAccessor">official Angular docs</a> we see that this interface is quite simple:</p><pre>interface <a href="https://angular.io/api/forms/ControlValueAccessor">ControlValueAccessor</a> {   <br>  <a href="https://angular.io/api/forms/ControlValueAccessor#writeValue"><strong>writeValue</strong>(obj: any): void</a>   <br>  <a href="https://angular.io/api/forms/ControlValueAccessor#registerOnChange"><strong>registerOnChange</strong>(fn: any): void</a>   <br>  <a href="https://angular.io/api/forms/ControlValueAccessor#registerOnTouched"><strong>registerOnTouched</strong>(fn: any): void</a>   <br>  <a href="https://angular.io/api/forms/ControlValueAccessor#setDisabledState">setDisabledState(isDisabled: boolean)?: void</a> <br>}</pre><p>I will not discuss the details of implementing a custom reactive Angular form control implementations — you can read about that <a href="https://alligator.io/angular/custom-form-control/">here</a>.</p><p>All we have to do is to define our writeValue method (it should apply formatting on value assignment) and register our directive as a NG_VALUE_ACCESSOR Angular provider. Here’s how it will look:</p><pre>import {Directive, ElementRef, forwardRef, HostListener, Input} from &#39;@angular/core&#39;;<br>import {MAT_INPUT_VALUE_ACCESSOR} from &#39;@angular/material&#39;;<br>import {NG_VALUE_ACCESSOR} from &#39;@angular/forms&#39;;<br>import {numberWithCommas} from &#39;./helpers&#39;;<br><br>@Directive({<br>  selector: &#39;input[matInputCommified]&#39;,<br>  providers: [<br>    {provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: MatInputCommifiedDirective},<br>    {<br>      provide: NG_VALUE_ACCESSOR,<br>      useExisting: forwardRef(() =&gt; MatInputCommifiedDirective),<br>      multi: true,<br>    }<br>  ]<br>})<br>export class MatInputCommifiedDirective {<br>  // tslint:disable-next-line:variable-name<br>  private _value: string | null;<br><br>  constructor(private elementRef: ElementRef&lt;HTMLInputElement&gt;,<br>  ) {<br>    <em>console</em>.log(&#39;created directive&#39;);<br>  }<br><br><br>  get value(): string | null {<br>    return this._value;<br>  }<br><br>  @Input(&#39;value&#39;)<br>  set value(<em>value</em>: string | null) {<br>    this._value = <em>value</em>;<br>    this.formatValue(<em>value</em>);<br>  }<br><br>  private formatValue(<em>value</em>: string | null) {<br>    if (<em>value </em>!== null) {<br>      this.elementRef.nativeElement.value = numberWithCommas(value);<br>    } else {<br>      this.elementRef.nativeElement.value = &#39;&#39;;<br>    }<br>  }<br><br>  private unFormatValue() {<br>    const value = this.elementRef.nativeElement.value;<br>    this._value = value.replace(/[^\d.-]/g, &#39;&#39;);<br>    if (value) {<br>      this.elementRef.nativeElement.value = this._value;<br>    } else {<br>      this.elementRef.nativeElement.value = &#39;&#39;;<br>    }<br>  }<br><br>  @HostListener(&#39;input&#39;, [&#39;$event.target.value&#39;])<br>  onInput(<em>value</em>) {<br>    this._value = <em>value</em>.replace(/[^\d.-]/g, &#39;&#39;);<br>    this._onChange(this._value); <strong>// here to notify Angular Validators</strong><br>  }<br><br>  @HostListener(&#39;blur&#39;)<br>  _onBlur() {<br>    this.formatValue(this._value);<br>  }<br><br>  @HostListener(&#39;focus&#39;)<br>  onFocus() {<br>    this.unFormatValue();<br>  }<br><br>  _onChange(<em>value</em>: any): void {<br>  }<br><br>  writeValue(<em>value</em>: any) {<br>    this._value = <em>value</em>;<br>    this.formatValue(this._value); <strong>// format Value</strong><br>  }<br><br>  registerOnChange(<em>fn</em>: (<em>value</em>: any) =&gt; void) {<br>    this._onChange = <em>fn</em>;<br>  }<br><br>  registerOnTouched() {<br>  }<br><br>}</pre><p>The code is quite self-explanatory but please note that to make Angular run validators against the entered value we should run the registered _onChange handler every time the value is changed. So we call it inside <em>onInput</em> method.</p><p>Let&#39;s check how our directive works now:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/367/1*wQ_bxSFxnjzqRK-iSwMaIw.gif" /></figure><p>LGTM!</p><h4>Publishing matInputCommified directive in the <strong>ngx-material-tools</strong> library</h4><p>To be able to use our directive in other projects I&#39;ve published it as part of the <a href="https://www.npmjs.com/package/ngx-material-tools"><em>ngx-material-tools</em></a> package.</p><p>You can use it this way:</p><pre>npm i matInputCommified<br>....</pre><pre>// in app.module.ts<br>...<br>imports: [<br>  BrowserModule,<br>  BrowserAnimationsModule,<br>  MatCardModule,<br>  MatInputModule,<br>  MatFormFieldModule,<br>  MatButtonModule,<br>  <strong>NgxMaterialToolsModule</strong>, // &lt;-- here is our module<br>  ReactiveFormsModule<br>],<br>...</pre><pre>&lt;!-- app.component.html --&gt;<br>&lt;form [formGroup]=&quot;myForm&quot; style=&quot;<em>margin-top</em>: 20px&quot;&gt;<br>  &lt;mat-form-field appearance=&quot;outline&quot;&gt;<br>    &lt;mat-label&gt;Deposit Amount&lt;/mat-label&gt;<br>    &lt;input matInput<br>           <strong>matInputCommified</strong> <br>           formControlName=&quot;deposit&quot;<br>           type=&quot;text&quot;/&gt;<br>  &lt;/mat-form-field&gt;<br>&lt;/form&gt;</pre><p>You can check how it works in <a href="https://stackblitz.com/edit/material-matinput-commified?file=app%2Fapp.component.ts">this playground</a>. Voila!</p><p>Did you like this article? <strong><em>Clap </em></strong>🤓</p><p>Like this article? <a href="https://buymeacoffee.com/nqypxi6"><strong>Buy me a coffee</strong></a> :-)</p><h3>Further reading</h3><ol><li><a href="https://alligator.io/angular/custom-form-control/">Using ControlValueAccessor to Create Custom Form Controls in Angular</a></li><li><em>MatDatePicker</em> input <a href="https://github.com/angular/components/blob/49a1324acc05cec1c5ff28d729abfe590f6772dd/src/material/datepicker/datepicker-input.ts#L294-L296">source code</a></li><li><em>Yaml-input</em> directive <a href="https://github.com/ha4us/ha4us/blob/91eb7e0f98f5dc6340cbb4e44e58544593668273/projects/gui/src/app/ha4us/directives/yaml-input.directive.ts">source code</a></li><li><a href="https://netbasal.com/adding-integrated-validation-to-custom-form-controls-in-angular-dc55e49639ae">Adding Integrated Validation to Custom Form Controls in Angular</a></li></ol><h4>Conclusion</h4><p>Hope someone read this text at the end of the article — if you’re one of those peeps then Merry Christmas to you and happy holidays!🎅</p><figure><a href="https://ukr.us20.list-manage.com/subscribe?u=51363eb47063b2905f3ba0cf7&amp;id=d58900bdac"><img alt="" src="https://cdn-images-1.medium.com/max/594/1*a601SFy8KekkveKtqNXbrw.png" /></a></figure><p><strong>Special thanks to </strong><a href="https://medium.com/u/d59a9e801370"><strong>Alexey Zuev</strong></a><strong> and </strong><a href="https://medium.com/u/f0e7507974eb"><strong>Lars Gyrup Brink Nielsen</strong></a><strong> for helping with and reviewing the article!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ebcbb7b027f4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/angular-in-depth/angular-material-matinput-control-with-thousands-separation-ebcbb7b027f4">Angular Material matInput control with thousands separator</a> was originally published in <a href="https://medium.com/angular-in-depth">Angular In Depth</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[So what is RxJS VirtualTimeScheduler?]]></title>
            <link>https://medium.com/angular-in-depth/so-what-is-rxjs-virtualtimescheduler-796e92ed722f?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/796e92ed722f</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[rxjs]]></category>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Wed, 06 Nov 2019 15:02:36 GMT</pubDate>
            <atom:updated>2020-04-29T20:37:14.787Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/743/0*sWOxXqYhCzGWZzfi.png" /></figure><blockquote><a href="https://medium.com/angular-in-depth"><strong><em>AngularInDepth</em></strong></a><strong><em> is moving away from Medium. More recent articles are hosted on the new platform </em></strong><a href="https://indepth.dev/angular/"><strong><em>inDepth.dev</em></strong></a><strong><em>. Thanks for being part of indepth movement!</em></strong></blockquote><p><em>You may already know that Observables produce values over time. And the moment when exactly value will be emitted is controlled by a special entity — scheduler. But what is VirtualTimeScheduler? Is it possible to emit values in a virtual time? More about it in this article!</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AqhO8ZKQ_vRnVeNd8X-7BA.png" /></figure><pre><strong><em>Prerequisites:</em></strong> you can read about RxJS Schedulers in <a href="https://blog.cloudboost.io/so-how-does-rx-js-queuescheduler-actually-work-188c1b46526e"><strong>this article</strong></a> or can watch <a href="https://www.youtube.com/watch?v=XizAnk1KItU&amp;list=PLNadw4d8-KMX8dVIJfdOg4kJMIw0mDriP&amp;index=4&amp;t=3s"><strong>this video</strong></a>.</pre><h3>Why?</h3><p>To understand why you may need VirtualTimeScheduler first let&#39;s try to test such code:</p><pre>getData(<em>timeSec</em>) {<br>  return this.http.get(&#39;some_url&#39;)<br>    .pipe(<br>      repeatWhen((<em>n</em>) =&gt; <em>n</em>.pipe(<br>        <strong>delay(<em>timeSec </em>* 1000)</strong>,<br>        take(2)<br>      ))<br>    );<br>}</pre><p>Here we make HTTP call with this.http.get function and if it succeeds it will repeat HTTP calls two more times after specified timeout.</p><p>So obviously AsyncScheduler is used by RxJS <a href="https://rxjs.dev/api/operators/delay"><em>delay</em></a> operator. (You remember — you can get familiar with schedulers <a href="https://blog.cloudboost.io/so-how-does-rx-js-queuescheduler-actually-work-188c1b46526e"><strong>here</strong></a> or <a href="https://www.youtube.com/watch?v=XizAnk1KItU&amp;list=PLNadw4d8-KMX8dVIJfdOg4kJMIw0mDriP&amp;index=4&amp;t=3s"><strong>here</strong></a> 😎)</p><p>The simplest method for testing async code is by using <a href="https://jasmine.github.io/tutorials/async#callbacks">jasmine <em>done</em> callback</a>. When a specific test is executed, jasmine holds and waits until <strong><em>done</em></strong> callback is called. So if data is provided asynchronously — we subscribe to them, run assertion when after we got all the data and then run done callback to say jasmine that we can continue. The test may look like this:</p><pre>it(&#39;should emit 3 specific values&#39;, (<em>done</em>) =&gt; {<br>  const range$ = service.<strong>getData(0.01)</strong>;<br>  const result = [];<br>  mockHttp = {get: () =&gt; of(42, asyncScheduler)};<br><br><br>  range$.subscribe({<br>    next: (<em>value</em>) =&gt; {<br>      result.push(<em>value</em>);<br>    },<br>    complete: () =&gt; {<br>      expect(result).toEqual([42, 42, 42]);<br>      <strong><em>done</em>();</strong><br>    }<br>  });<br><br>});</pre><p>And in this example a drawback of jasmine done callback is becoming obvious — we cannot test code that has some big delays time. Since otherwise tests run will be too long.</p><p>How to omit it?</p><p>We can provide a smaller delay time. That&#39;s why in test code you can see that we call getData with very little value. You may ask — how to test code that uses big-time delays? And before RxJS version 6 provided TestScheduler.run method and time-progressive syntax marbles (you remember — this article is not about marble testing but about VirtualTimeScheduler😉)— we could use RxJS VirtualTimeScheduler for that.</p><pre>If you are interesting in TestScheduler.run and all this modern testing RxJS staff - you may read nice articles by <a href="https://blog.angularindepth.com/@kevinkreuzer"><strong>@kevinkreuzer</strong></a> or you can watch my free video-course: &quot;<a href="https://mailchi.mp/9c8299ec62c2/rxjsunittesting"><strong>RxJS unit testing in Angular application. The whole picture.</strong></a>&quot;</pre><h3>VirtualTimeScheduler</h3><p>So testing Observables with jasmine <em>done</em> callback method is not good for big delay values. And this is where VirtualTimeScheduler can help us. It provides a virtual time mechanism so you can emulated passed time inside RxJS scheduler to run delayed tasks instantly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LHK6CP7LKnLPlf-rn4wOxQ.png" /><figcaption>How VirtualTimeScheduler works</figcaption></figure><p>In RxJS If we want to emit values with some specified delays — then AsyncScheduler is used. AsyncScheduler uses setInterval internally to schedule value emissions. What we need for testing such code is to make somehow run scheduled tasks instantly but with keeping the order of values.</p><p>VirtualTimeScheduler can do that since it inherits from AsyncScheduler and re-defined some internal methods.</p><p>If we replace AsyncScheduler instance with VirtualTimeScheduler instance in our function — then VirtualTimeScheduler prevents calling real setInterval and put a task in an internal queue (sorted by delay)</p><p>And when we call VirtualTimeScheduler instance flush function — all the delayed values will be emitted with keeping the order of values. Here you can see how it is implemented under the hood:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hQIj-1AiNKtY0MLhisY18w.png" /></figure><p>You may observable that we can also specify <strong><em>maxFrames</em></strong> threshold, so only specific tasks are executed which are delayed not more than the threshold value.</p><p>Now our code has to be changed — new <em>scheduler</em> param is added:</p><pre>getData(<em>timeSec</em>, <strong><em>scheduler </em>= asyncScheduler</strong>) {<br>  return this.http.get(&#39;some_url&#39;)<br>    .pipe(<br>      repeatWhen((<em>n</em>) =&gt; <em>n</em>.pipe(<br>        delay(<em>timeSec </em>* 1000, <em>scheduler</em>),<br>        take(2)<br>      ))<br>    );<br>}</pre><p>Test code will look like:</p><pre>it(&#39;should emit 3 specific values&#39;, () =&gt; {<br>  const scheduler = new <strong>VirtualTimeScheduler</strong>();<br>  service.http = {get: () =&gt; of(42, <strong>scheduler</strong>)};<br><br>  const range$ = service.getData(30, <strong>scheduler</strong>); // scheduler param<br>  const result = [];<br><br>  range$.subscribe({<br>    next: (<em>value</em>) =&gt; {<br>      result.push(<em>value</em>);<br>    }<br>  });<br><br>  <strong>scheduler.flush();</strong><br>  expect(result).toEqual([42, 42, 42]);<br>});</pre><p>Here are steps for writing unit tests with VirtualTimeScheduler:</p><ol><li>We feed VirtualTimeScheduler to our code instead of AsyncScheduler. So there should be another scheduler param in our methods.</li></ol><p>2. Then we get observable and subscribe to it. (you remember that cold observable is started only on subscription)</p><p>3. After that, we call <strong><em>flush()</em></strong> method that makes virtual time pass.</p><p>4. And check the final result</p><h3>VirtualTimeScheduler vs TestScheduler</h3><p>VirtualTimeScheduler has a child class — TestScheduler. TestScheduler has methods for marble testing.</p><p>Since TestScheduler is a child class of VirtualTimeScheduler we can take our tests with VirutlaTimeScheduler and replace its instance with TestScheduler instance.</p><p>But there are some small things you have to know not to waste time for possible issues debugging:</p><ol><li>Testscheduler <strong><em>maxFrame</em></strong> value is set to 750 milliseconds — this is done as sanity checks for marble testing. So if your code delays are bigger then that value — you should reassign it. In our case, since we do not run marble testing — we have to assign Infinity value to prevent test blocking.</li></ol><p>2. TestScheduler also demands assertion expression when you want to create an instance. This assertion is used in marble testing.</p><p>Not to test your patience — I provide the same test code with TestScheduler:</p><pre>it(&#39;should emit 3 specific values&#39;, () =&gt; {<br>  const assertion = (<em>actual</em>, <em>expected</em>) =&gt; {<br>    expect(<em>actual</em>).toEqual(<em>expected</em>);<br>  };<br>  const scheduler = <strong>new TestScheduler(assertion);</strong><br>  scheduler.<strong>maxFrames = <em>Number</em>.POSITIVE_INFINITY</strong>;<br><br>  service.http = {get: () =&gt; of(42, <strong>scheduler</strong>)};<br><br>  const range$ = service.getData(30, <strong>scheduler</strong>);<br>  const result = [];<br><br>  range$.subscribe({<br>    next: (<em>value</em>) =&gt; {<br>      result.push(<em>value</em>);<br>    }<br>  });<br><br>  <strong>scheduler.flush();</strong><br>  expect(result).toEqual([42, 42, 42]);<br>});</pre><p>Using TestScheduler just like VirtualTimeScheduler has the same imperfection — it is not visual and we can check only the final result. But marble testing and progressive time syntax can solve all these challenges. How? You can find it in free video-course &quot;<a href="https://mailchi.mp/9c8299ec62c2/rxjsunittesting"><strong>RxJS unit testing in Angular application. The whole picture.</strong></a><strong>&quot;</strong></p><h3>Conclusion</h3><p>Time to sum up.</p><p>VirtualTimeScheduler method has such <strong>pros</strong>:</p><ol><li>We can provide real production delay values</li><li>We can test even hardcoded values — since timespans will pass instantly.</li></ol><p>But this method has such imperfections:</p><ol><li>It allows testing only the final result (after Observable is complete).</li><li>And additional method param is needed</li><li>&quot;Using the VirtualTimeScheduler won’t help if the code under test also includes time-related, non-RxJS code, as the virtual (RxJS level mocking) and fake time (setTimeout, setInterval monkey patching) concepts differ significantly.&quot; by <a href="https://medium.com/u/d05557088657">Nicholas Jamieson</a><br>To omit this con you should use <a href="https://netbasal.com/testing-asynchronous-code-in-angular-using-fakeasync-fc777f86ed13">fakeAsync Angular helper function</a> instead of VirtualTimeScheduler.</li></ol><pre>*<strong>Remarks:</strong> After releasing RxJS version 6 with TestScheduler.run method you should definitely use marble testing for making unit-tests. All test examples with VirtualTimeScheduler in this article were provided just to understand the topic better. But of cause may be used if you find it suitable.</pre><h3>More to read</h3><ol><li>&quot;<a href="https://dev.to/itnext/tools-for-rxjs-code-unit-testing-in-angular-8-apps-free-udemy-video-course-3bbj">Tools for RxJS code unit testing in Angular 8 apps</a>&quot;</li><li>&quot;<a href="https://blog.angularindepth.com/rxjs-testing-with-fake-time-94114271eed2">RxJS: Testing with Fake Time</a>&quot;</li><li>&quot;<a href="https://netbasal.com/testing-asynchronous-code-in-angular-using-fakeasync-fc777f86ed13">Testing Asynchronous Code in Angular Using FakeAsync</a>&quot;</li><li>Articles by <a href="https://medium.com/u/6cd4fff6b841">Kevin Kreuzer</a> about RxJS unit testing.</li></ol><p>Did you like the article? Clap👏 🤓</p><p>Let’s keep in touch on <a href="https://twitter.com/El_Extremal"><strong>Twitter</strong></a>!</p><p>*BTW. I started a video tutorial series “<a href="https://www.youtube.com/watch?v=HI5f6naJdJ8&amp;list=PLNadw4d8-KMVSOffiYBuOlzvF38sO9pdu&amp;utm_content=educational&amp;utm_campaign=2019-11-13&amp;utm_source=email-sendgrid&amp;utm_term=8565702&amp;utm_medium=2586716"><strong>Angular can waste your time</strong></a>” on Youtube. There I will be publishing videos about resolving tricky cases with Angular and RxJS. <a href="https://www.youtube.com/watch?v=HI5f6naJdJ8&amp;list=PLNadw4d8-KMVSOffiYBuOlzvF38sO9pdu&amp;utm_content=educational&amp;utm_campaign=2019-11-13&amp;utm_source=email-sendgrid&amp;utm_term=8565702&amp;utm_medium=2586716"><strong>Take a look!</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=796e92ed722f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/angular-in-depth/so-what-is-rxjs-virtualtimescheduler-796e92ed722f">So what is RxJS VirtualTimeScheduler?</a> was originally published in <a href="https://medium.com/angular-in-depth">Angular In Depth</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wrapping CommonJS library in Angular 8 directive on the example of mark.js]]></title>
            <link>https://medium.com/angular-in-depth/wrapping-commonjs-library-in-angular-8-directive-on-the-example-of-mark-js-976cbcd5d10a?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/976cbcd5d10a</guid>
            <category><![CDATA[commonjs]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Wed, 16 Oct 2019 07:41:05 GMT</pubDate>
            <atom:updated>2020-04-29T20:42:41.025Z</atom:updated>
            <content:encoded><![CDATA[<p><em>And enhancing its functionality with custom logic.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CZ7HWAknEzo-aHF3iGd6AA.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@kadh?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Kira auf der Heide</a></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/743/0*omH3311_oe4K_DiA.png" /></figure><blockquote><a href="https://medium.com/angular-in-depth"><strong><em>AngularInDepth</em></strong></a><strong><em> is moving away from Medium. More recent articles are hosted on the new platform </em></strong><a href="https://indepth.dev/angular/"><strong><em>inDepth.dev</em></strong></a><strong><em>. Thanks for being part of indepth movement!</em></strong></blockquote><p><strong><em>Prerequisites: </em></strong><em>you should be familiar with the </em><strong><em>Angular </em></strong><em>framework</em><strong><em> </em></strong><em>and</em><strong><em> </em></strong><a href="https://cli.angular.io/"><strong><em>Angular CLI</em></strong></a><strong><em>.</em></strong></p><h3>Introduction</h3><p>Time to time on my daily tasks I have to implement some functionality that was already implemented by someone previously in a neat vanillaJS library, but… no Angular version or even ES6 module of it is available to be able to easily grab it into your Angular 8 application.</p><p>Yes, you can attach this lib in index.html with <em>script </em>tag but from my point of view, it hardens maintainability. Also, you should do the same for another Angular project where you might use it.</p><p>Much better for is create Angular wrapper directive (or component) and publish it as npm package so everyone (and you of course:) can easily re-use it in another project.</p><p>One of such libraries is <a href="https://markjs.io/"><strong><em>mark.js </em></strong></a>— quite solid solution for highlighting search text inside a specified webpage section.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/644/1*bB6xcbX7qoL37eFoxDD0sw.png" /><figcaption>mark.js</figcaption></figure><h3>How mark.js works</h3><p>In original implementation mark.js can be connected to a project in two ways:</p><pre>$ npm install <strong>mark</strong>.js --save-dev</pre><pre>// in JS code<br><strong>const</strong> <strong>Mark</strong> = require(&#39;mark.js&#39;);<br><strong>let</strong> instance = new <strong>Mark</strong>(document.querySelector(&quot;div.context&quot;));<br>instance.mark(keyword [, options]);</pre><pre><strong>OR</strong></pre><pre><strong>&lt;script src=&quot;vendor/mark.js/dist/mark.min.js&quot;&gt;&lt;/script&gt;</strong></pre><pre>// in JS code<br><strong>let</strong> instance = new <strong>Mark</strong>(document.querySelector(&quot;div.context&quot;));<br>instance.mark(keyword [, options]);</pre><p>And the result looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/565/1*JKe-77FJMvnm54T3KjNeJQ.png" /><figcaption>mark.js run result (taken from <a href="https://markjs.io/configurator.html">official Mark.js page</a>)</figcaption></figure><p>You can play with it more on mark.js <a href="https://markjs.io/configurator.html">configurator page</a>.</p><p>But can we use it in <strong><em>Angular</em></strong> way? Say, like this</p><pre>// some Angular module<br>imports: [<br>...<br>MarkjsModule // imports markjsHighlightDirective<br>...<br>]</pre><pre>// in some component template<br>&lt;div class=&quot;content_wrapper&quot; <br><strong>     [markjsHighlight]=&quot;searchValue&quot;<br>     [markjsConfig]=&quot;config&quot;</strong><br>&gt;</pre><p>Let&#39;s also add some additional functionality. Say, scroll content_wrapper to first highlighted word:</p><pre>&lt;div class=&quot;content_wrapper&quot; <br><strong>     [markjsHighlight]=&quot;searchText&quot;<br>     [markjsConfig]=&quot;config&quot;<br>     [scrollToFirstMarked]=&quot;true&quot;<br></strong>&gt;</pre><p>Now let&#39;s implement and publish Angular library with a demo application that will contain <strong><em>markjsHighlightDirective</em></strong> and its module. <br>We will name it <strong>ngx-markjs.</strong></p><h3>Planning Angular project structure</h3><p>To generate an Angular project for our lib we will use Angular CLI.</p><pre><strong>npm install -g @angular/cli</strong></pre><p>Now let&#39;s create our project and add ngx-markjs lib to it:</p><pre><strong>ng new ngx-markjs-demo --routing=false --style=scss<br></strong>// a lot of installations goes here</pre><pre><strong>cd ngx-markjs-demo</strong></pre><pre><strong>ng generate lib ngx-markjs</strong></pre><p>And now lets add <strong><em>markjsHighlightDirective</em></strong> starter to our <em>ngx-markjs </em>lib</p><pre>ng generate directive <strong><em>markjsHighlight</em></strong> --project=ngx-markjs</pre><p>After deleting <em>ngx-markjs.component.ts</em> and <em>ngx-markjs.service.ts</em> in <em>projects/ngx-markjs/src/lib/</em> folder which were created automatically by Angular CLI we will get next directory structure for our project:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/417/1*pS1dQXz6-pB9ayx6UPNQKA.png" /><figcaption>ngx-markjs-demo project with ngx-markjs lib</figcaption></figure><p>To conveniently build our library lets add two more lines in a project <em>package.json</em> file to <em>scripts</em> section:</p><pre>&quot;scripts&quot;: {<br>  &quot;ng&quot;: &quot;ng&quot;,<br>  &quot;start&quot;: &quot;ng serve --port 4201&quot;,<br>  &quot;build&quot;: &quot;ng build&quot;,<br><strong>  &quot;build:ngx-markjs&quot;: &quot;ng build ngx-markjs &amp;&amp; npm run copy:lib:dist&quot;, <br>  &quot;copy:lib:dist&quot;: &quot;cp -r ./projects/ngx-markjs/src ./dist/ngx-markjs/src&quot;</strong>,<br>  &quot;test&quot;: &quot;ng test&quot;,<br>  &quot;lint&quot;: &quot;ng lint&quot;,<br>  &quot;e2e&quot;: &quot;ng e2e&quot;<br>},</pre><p><strong>build:ngx-markjs — </strong>runs build for ngx-markjs library (but not for parent demo project)</p><p><strong>copy:lib:dist —</strong> it is convenient to have source files in npm packages as well, so this command will copy library sources to <strong><em>/dist/ngx-markjs </em></strong>folder (where compiled module will be placed after <strong><em>build:ngx-markjs</em></strong> command).</p><p>Now time to add implementation code!</p><p><strong>*Remark</strong>: <em>official Angular documentation about </em><a href="https://angular.io/guide/creating-libraries"><em>creating libraries</em></a><em> recommends generating starter without main parent project, like this: </em><br><strong>ng new my-workspace — create-application=false</strong><br><em>But I decided to keep the main project and make it a demo application just for my convenience.</em></p><figure><a href="http://eepurl.com/gHF0av"><img alt="" src="https://cdn-images-1.medium.com/max/594/1*33AyiLH06Tzu-9KxL1hZjg.png" /></a></figure><h3>Connecting commonJS lib into Angular app</h3><p>We need to do a few preparational steps before we start implementing our directive:</p><p><strong>#1. Load mark.js</strong></p><p>Mark.js library which we want to wrap is provided in <a href="https://flaviocopes.com/commonjs/">CommonJS</a> format.</p><p>There are two ways to connect script in <a href="https://flaviocopes.com/commonjs/">CommonJS</a> script:</p><p><strong>a)</strong> Add it with <strong>script</strong> tag to index.html:</p><pre><strong>&lt;script src=&quot;vendor/mark.js/dist/mark.min.js&quot;&gt;&lt;/script&gt;</strong></pre><p><strong>b)</strong> Add it to angular.json file in a project root so Angular builder will grab and applied it (as if it was included with a <em>script</em> tag)</p><pre>&quot;sourceRoot&quot;: &quot;src&quot;,<br>&quot;prefix&quot;: &quot;app&quot;,<br>&quot;architect&quot;: {<br>  &quot;build&quot;: {<br>    &quot;builder&quot;: &quot;@angular-devkit/build-angular:browser&quot;,<br>    &quot;options&quot;: {<br>      &quot;outputPath&quot;: &quot;dist/ngx-markjs-demo&quot;,<br>      &quot;index&quot;: &quot;src/index.html&quot;,<br>      &quot;main&quot;: &quot;src/main.ts&quot;,<br>      &quot;polyfills&quot;: &quot;src/polyfills.ts&quot;,<br>      &quot;tsConfig&quot;: &quot;tsconfig.app.json&quot;,<br>      &quot;aot&quot;: false,<br>      &quot;assets&quot;: [<br>        &quot;src/favicon.ico&quot;,<br>        &quot;src/assets&quot;<br>      ],<br>      &quot;styles&quot;: [<br>        &quot;src/styles.scss&quot;<br>      ],<br>      <strong>&quot;scripts&quot;: []</strong><br>    },</pre><p><strong>#2. Adding mark.js to lib package.json</strong></p><p>Now we should add <em>mark.js</em> lib as a dependency to our library package.json in <strong><em>&lt;root&gt;/projects/ngx-markjs/src </em></strong>folder (don&#39;t mix it up with src/package.json — file for main parent project).<br>We can add it as <strong><em>peerDependencies </em></strong>section — in that case, you should install mark.js manually prior to installing our wrapper package.</p><p>Or we can add mark.js to <strong><em>dependencies</em></strong> section — then mark.js package will be installed automatically when you run <strong><em>npm i ngx-markjs.</em></strong></p><p>You can read more about the difference between package.json <strong><em>dependencies</em></strong> and <strong><em>peerDependencies</em></strong> in <a href="https://blog.angularindepth.com/npm-peer-dependencies-f843f3ac4e7f">this great article</a>.</p><p><strong>#3. Get entity with <em>require</em> call.</strong></p><pre><strong>const Mark = require(&#39;mark.js&#39;);</strong></pre><p>In our case, I would prefer to use <strong>require</strong> since mark.js code should be present only inside <strong><em>markjsHighlight </em></strong>lib module but not in whole application (until we use actually it there)<em>.</em></p><p><strong><em>Small remark:</em></strong><em> some tslint configurations prevent using </em><strong><em>require </em></strong><em>to stimulate using es6 modules, so in that case just wrap </em><strong><em>require </em></strong><em>with</em><strong><em> /* tslint: disabled */ </em></strong><em>comment</em><strong><em>.</em></strong><em> Like this:</em></p><pre>/* tslint:disable */<br><strong>const Mark = require(&#39;mark.js&#39;);<br></strong>/* tslint:enable */</pre><p>The project is ready. Now it is time to implement our <strong><em>markjsHighlightDirective.</em></strong></p><h3>Wrapping mark.js in a directive</h3><p>Ok, so lets plan how our <strong><em>markjsHighlightDirective </em></strong>will work:</p><ol><li>It should be applied to the element with content — to get HTML element content where the text will be searched. (<strong>markjsHighlight </strong>input)</li><li>It should accept <a href="https://markjs.io/">mark.js configuration object</a> (<strong>markjsConfig </strong>input)</li><li>And we should be able to switch on and off &#39;<em>scroll to marked text</em>&#39; feature (<strong>scrollToFirstMarked </strong>input)</li></ol><p>For example:</p><pre>&lt;div class=&quot;content_wrapper&quot; <br><strong>     [markjsHighlight]=&quot;searchText&quot;<br>     [markjsConfig]=&quot;config&quot;<br>     [scrollToFirstMarked]=&quot;true&quot;<br></strong>&gt;</pre><p>Now it is time to implement these requirements.</p><h4>Adding mark.js to the library</h4><p>Install mark.js to our project</p><pre>npm install mark.js</pre><p>And create its instance in a projects/ngx-markjs/src/lib/markjs-highlight.directive.ts file:</p><pre>import {Directive} from &#39;@angular/core&#39;;<br><br><br>declare var require: any;<br>const Mark = require(&#39;mark.js&#39;);<br><br><br>@Directive({<br>  selector: &#39;[markjsHighlight]&#39;<br>})<br>export class MarkjsHighlightDirective {<br><br>  constructor() {}<br><br>}</pre><p>To prevent Typescript warnings — I declared <strong>require</strong> global variable.</p><h4>Creating a basic directive starter</h4><p>The very first starter for MarkjsHighlightDirective will be</p><pre>@Directive({<br>  selector: &#39;[markjsHighlight]&#39; <strong>// our directive</strong><br>})<br>export class MarkjsHighlightDirective implements OnChanges {<br><br>  @Input() markjsHighlight = &#39;&#39;; <strong> // our inputs</strong><br>  @Input() markjsConfig: any = {};<br>  @Input() scrollToFirstMarked: boolean = false;<br><br>  @Output() getInstance = new EventEmitter&lt;any&gt;();<br><br>  markInstance: any;<br><br>  constructor(<br>    private contentElementRef: ElementRef, <strong>// host element ref</strong><br>    private renderer: Renderer2 <strong>// we will use it to scroll</strong><br>  ) {<br>  }<br><br>  ngOnChanges(<em>changes</em>) {  <strong>//if searchText is changed - redo marking</strong></pre><pre>    if (!this.markInstance) { <strong>// emit mark.js instance (if needeed)</strong><br>      this.markInstance = new Mark(this.contentElementRef.nativeElement);<br>      this.getInstance.emit(this.markInstance);<br>    }<br><br>    this.hightlightText(); <strong>// should be implemented</strong></pre><pre>    if (this.scrollToFirstMarked) {<br>      this.scrollToFirstMarkedText();<strong>// should be implemented</strong><br>    }    <br>  }<br>}</pre><p>Ok, so let&#39;s go through this starter code:</p><ol><li>We defined three inputs for searchText value, config and scrolling on/off functionality (as we planned earlier)</li><li>ngOnChanges lifeCycle hook emits instance of Mark.js to parent component (in case you want to implement some additional Mark.js behavior)<br>Also, each time searchText is changed we should redo text highlight (since search text is different now) — this functionality will be implemented in <strong><em>this.hightlightText</em></strong> method.<br>And if scrollToFirstMarked is set to true — then we should run <strong><em>this.scrollToFirstMarkedText</em></strong>.</li></ol><h4>Implementing highlight functionality</h4><p>Our method this.<strong><em>hightlightText </em></strong>should receive searchText value, unmark previous search results and do new text highlighting. It can be successfully done with this code:</p><pre>hightlightText() {<br>  this.markjsHighlight = this.markjsHighlight || &#39;&#39;; </pre><pre>  if (this.markjsHighlight &amp;&amp; this.markjsHighlight.length &lt;= 2) {<br>    this.markInstance.unmark();<br>    return;</pre><pre>  } else {</pre><pre>    this.markInstance.unmark({<br>      done: () =&gt; {<br>        this.markInstance.mark((this.markjsHighlight || &#39;&#39;), this.markjsConfig);<br>      }<br>    });<br>  }<br>}</pre><p>Code is self-explanatory: we check if markjsHighlight valur is not null or undefined (because with these values Mark.js instances throw the error).</p><p>Then check for text length. If it is just one letter or no text at all — we unmark text and return;</p><p>Otherwise, we unmark previously highlighted text and start new highlighting process.</p><h4>Implementing a &quot;scroll to first marked result&quot; feature</h4><p>One important remark here before we start implementing scroll feature: content wrapper element, where we apply our directive to should have css position set other than static (for example<strong> </strong><em>position: relative</em>). Otherwise offset to be scrolled to will be calculated improperly.</p><p>OK, lets code <strong><em>this.scrollToFirstMarkedText </em></strong>method:</p><pre>constructor(<br><strong>  private contentElementRef: ElementRef,<br>  private renderer: Renderer2</strong><br>) {<br>}<br>....</pre><pre><strong>scrollToFirstMarkedText</strong>() {<br>  const content = this.contentElementRef.nativeElement;</pre><pre><strong>// calculating offset to the first marked element<br></strong>  const firstOffsetTop = (content.querySelector(&#39;mark&#39;) || {}).offsetTop || 0; </pre><pre>  this.<strong>scrollSmooth</strong>(content, firstOffsetTop); <strong>// start scroll</strong><br>}<br><br><strong>scrollSmooth</strong>(<em>scrollElement</em>, <em>firstOffsetTop</em>) {<br>  const renderer = this.renderer;<br><br>  if (cancelAnimationId) {<br>    cancelAnimationFrame(cancelAnimationId);<br>  }<br>  const currentScrollTop = <em>scrollElement</em>.scrollTop;<br>  const delta = <em>firstOffsetTop </em>- currentScrollTop;<br><br>  <strong>animate</strong>({<br>    duration: 500,<br>    timing(<em>timeFraction</em>) {<br>      return <em>timeFraction</em>;<br>    },<br>    draw(<em>progress</em>) {<br>      const nextStep = currentScrollTop + <em>progress </em>* delta;</pre><pre>     <strong>// set scroll with Angular renderer</strong><br>     renderer.setProperty(<em>scrollElement</em>, &#39;scrollTop&#39;, nextStep);<br>    }<br>  });<br>}</pre><pre>...<br>let cancelAnimationId;</pre><pre><strong>// helper function for smooth scroll</strong><br>function <strong>animate</strong>({<em>timing</em>, <em>draw</em>, <em>duration</em>}) {<br>  const start = <em>performance</em>.now();<br>  cancelAnimationId = requestAnimationFrame(function animate2(<em>time</em>) {<br>    // timeFraction goes from 0 to 1<br>    let timeFraction = (<em>time </em>- start) / <em>duration</em>;<br>    if (timeFraction &gt; 1) {<br>      timeFraction = 1;<br>    }<br>    // calculate the current animation state<br>    const progress = <em>timing</em>(timeFraction);<br>    <em>draw</em>(progress); // draw it<br>    if (timeFraction &lt; 1) {<br>      cancelAnimationId = requestAnimationFrame(animate2);<br>    }<br>  });<br>}</pre><p>How it works:</p><ol><li>We get content wrapper element (injected in a constructor by Angular) and query for first highlighted text node (Mark.js to highlight text wrap it in &lt;Mark&gt;&lt;/Mark&gt; HTML element).</li><li>Then start <strong><em>this.scrollSmooth</em></strong><em> </em>function. <strong><em>scrollSmooth</em></strong> cancels previous scroll (if any), calculates scroll difference, delta (diff between current scroll position and offsetTop of marked element) and call an animated function which will calculate timings for smooth scrolling and do actual scroll (by calling renderer.setProperty(<em>scrollElement</em>, ‘scrollTop’, nextStep)).</li><li><strong>Animate</strong> function is a helper taken from a very good javascript learning tutorial site <a href="https://javascript.info/js-animation">javscript.info</a>.</li></ol><p>Our directive is ready! You can take a look at a full code <a href="https://github.com/kievsash/ngx-markjs/blob/master/projects/ngx-markjs/src/lib/markjs-highlight.directive.ts">here</a>.</p><p>The only thing we have to do yet is to add a directive to <a href="https://github.com/kievsash/ngx-markjs/blob/master/projects/ngx-markjs/src/lib/ngx-markjs.module.ts">NgxMarkjsModule</a> module:</p><pre>import { NgModule } from &#39;@angular/core&#39;;<br>import { MarkjsHighlightDirective } from &#39;./markjs-highlight.directive&#39;;<br><br><br><br>@NgModule({<br>  declarations: [MarkjsHighlightDirective],<br>  imports: [<br>  ],<br>  exports: [MarkjsHighlightDirective]<br>})<br>export class NgxMarkjsModule { }</pre><h4>Applying Result</h4><p>Now let&#39;s use it in our demo application:</p><p>1. Import NgxMarkjsModule to <a href="https://github.com/kievsash/ngx-markjs/blob/master/src/app/app.module.ts"><strong><em>app.module.ts</em></strong></a><strong><em>:</em></strong></p><pre>...<br>import {<strong>NgxMarkjsModule</strong>} from &#39;ngx-markjs&#39;;<br><br>@NgModule({<br>  declarations: [<br>    AppComponent<br>  ],<br>  imports: [<br>    BrowserModule,<br>    <strong>NgxMarkjsModule</strong><br>  ],<br>  providers: [],<br>  bootstrap: [AppComponent]<br>})<br>export class AppModule { }</pre><p>2. I added some content to <a href="https://github.com/kievsash/ngx-markjs/blob/master/src/app/app.component.html"><strong><em>app.component.html</em></strong></a> and applied the directive to it:</p><pre>&lt;div class<strong>=&quot;search_input&quot;</strong>&gt;<br>  &lt;input placeholder<strong>=&quot;Search...&quot; </strong>#<em>search </em>type<strong>=&quot;text&quot;</strong>&gt;<br>&lt;/div&gt;<br>&lt;div class<strong>=&quot;content_wrapper&quot;<br>     [markjsHighlight]=&quot;searchText$ | async&quot;<br>     [markjsConfig]=&quot;searchConfig&quot;<br>     [scrollToFirstMarked]=&quot;true&quot;<br></strong>&gt;<br>  &lt;p&gt;Lorem ipsum dolor ssit amet, consectetur...a lot of text futher&lt;/p&gt;</pre><p>3. In <a href="https://github.com/kievsash/ngx-markjs/blob/master/src/app/app.component.ts"><strong><em>app.component.ts</em></strong></a> we should subscribe to input change event and feed search text to <strong>markjsHighlight </strong>directive with async pipe:</p><pre>@Component({<br>  selector: &#39;app-root&#39;,<br>  templateUrl: &#39;./app.component.html&#39;,<br>  styleUrls: [&#39;./app.component.scss&#39;]<br>})<br>export class AppComponent implements AfterViewInit {<br>  title = &#39;ngx-markjs-demo&#39;;<br> <strong> @ViewChild(&#39;search&#39;, {static: false}) searchElemRef: ElementRef;</strong><br>  searchText$: Observable&lt;string&gt;;<br>  searchConfig = {separateWordSearch: false};<br><br>  ngAfterViewInit() {<br>    // create stream from inpout change event with rxjs &#39;from&#39; function</pre><pre>    <strong>this.searchText$ = fromEvent(this.searchElemRef.nativeElement, &#39;keyup&#39;).pipe(<br>      map((<em>e</em>: Event) =&gt; (<em>e</em>.target as HTMLInputElement).value),<br>      debounceTime(300),<br>      distinctUntilChanged()<br>    );</strong><br>  }<br>}</pre><p>Let&#39;s start it and take a look at result:</p><pre>ng serve</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/942/1*63zqTlU9EXMtvAhywRcY3w.gif" /><figcaption>It works!</figcaption></figure><p>We did it!<br>The last thing to do: we should publish our directive to npm registry:</p><pre>npm login<br>npm build:ngx-markjs<br>cd ./dist/ngx-markjs<br>npm publish</pre><p>And here it is in a registry: <a href="https://www.npmjs.com/package/ngx-markjs">ngx-markjs.</a></p><h3>Conclusion</h3><p>Did you meet some neat vanillaJS library which you want to use in Angular? Now you know how to do that!</p><h4>Pros</h4><ol><li>Now we can easily import our directive in Angular 8 project.</li><li>Additional scroll functionality is quite neat — use it to improve user experience.</li></ol><h4>Cons</h4><ol><li>Possibly mark.js implemented only for a browser. So if you plan to use it in some other platforms (Angular allows it — read more about it <a href="https://blog.angularindepth.com/angular-platforms-in-depth-part-1-what-are-angular-platforms-9919d45f3054">here</a>) — it may not work.</li></ol><p><strong>Related links:</strong></p><ol><li><a href="https://markjs.io/">Mark.js</a></li><li><a href="https://github.com/kievsash/ngx-markjs">ngx-markjs github repo</a>.</li></ol><p><strong>More to read:</strong></p><ol><li><a href="https://blog.angularindepth.com/angular-platforms-in-depth-part-1-what-are-angular-platforms-9919d45f3054">Angular Platforms in depth. Part 1.</a></li><li><a href="https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5">Creating a Library with Angular CLI.</a></li><li><a href="https://blog.angularindepth.com/making-angular-project-mono-repo-with-ngrx-state-management-and-lazy-loading-3f09178319d4">Making an Angular project mono repo</a></li></ol><p>Hope you enjoyed the article. If yes —<a href="https://clicktotweet.com/tHsef"><strong>tweet about it</strong></a><strong> and </strong>follow me on <a href="https://twitter.com/El_Extremal"><strong>Twitter</strong></a>.</p><p><em>*It was originally published in </em><a href="https://www.codementor.io/alexanderposhtaruk/wrapping-commonjs-library-in-angular-8-directive-on-the-example-of-mark-js-zs9inqhtg?published=1"><em>codementor.io community blog</em></a><em>.</em></p><p><strong>Special thanks to </strong><a href="https://medium.com/u/802a7996f6b6"><strong>Tim Deschryver</strong></a><strong> and </strong><a href="https://medium.com/u/f0e7507974eb"><strong>Lars Gyrup Brink Nielsen</strong></a><strong> for reviewing this article.</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=976cbcd5d10a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/angular-in-depth/wrapping-commonjs-library-in-angular-8-directive-on-the-example-of-mark-js-976cbcd5d10a">Wrapping CommonJS library in Angular 8 directive on the example of mark.js</a> was originally published in <a href="https://medium.com/angular-in-depth">Angular In Depth</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TimeRange — RxJS custom function that emits a set of values in specified timeouts]]></title>
            <link>https://itnext.io/timerange-rxjs-custom-function-that-emits-a-set-of-values-in-specified-timeouts-bd6dd07974ce?source=rss-ae97ac398bf9------2</link>
            <guid isPermaLink="false">https://medium.com/p/bd6dd07974ce</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[rxjs]]></category>
            <dc:creator><![CDATA[Alexander Poshtaruk]]></dc:creator>
            <pubDate>Mon, 23 Sep 2019 11:32:27 GMT</pubDate>
            <atom:updated>2020-02-12T11:41:45.001Z</atom:updated>
            <content:encoded><![CDATA[<h3>&#39;timeRange&#39; — RxJS custom function that emits a set of values in specified timeouts</h3><p><em>Why, how and where for those who like RxJS :-)</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7hyHYXeUNMhJBjV2Q1N6yA.jpeg" /><figcaption>Customizing RxJS (Photo by <a href="https://unsplash.com/@sneakyelbow?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Sneaky Elbow</a>)</figcaption></figure><pre><strong>Prerequisites: I assume you know RxJS and have some practice with it. If you are new to it or want to learn some advanced topics - watch my video-course &quot;</strong><a href="https://www.udemy.com/course/hands-on-rxjs-for-web-development/"><strong>Hands-on RxJS for Web development</strong></a><strong>&quot; on Udemy.</strong></pre><h3>Why</h3><p>Periodically in my development practice, I meet situations when you have to emit a few values in specified timeouts.</p><p>For example:</p><ol><li>To show and then hide some splash-message:</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*2jn_yYTMGcpyLqeDxAG3CA.gif" /><figcaption>Managing splash-message show/hide timeouts</figcaption></figure><p>2. To do some nice animations</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*2klU0Bnmad1gZWWD3U20Wg.gif" /><figcaption>Managing appearance timeouts</figcaption></figure><p>3. Or even just emit values for unit tests (yeah, I know about <a href="https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/testing/marble-testing.md">marbles</a>, but sometimes I just want to check final value, period:)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fJ1bCfDzLkr74GNhIt1bcQ.png" /><figcaption>Simple code for auto-complete with debounce — demands providing values with specific timings to be tested</figcaption></figure><p>So how it can be implemented? Ok, let&#39;s review some scenarios:</p><h3>How to implement?</h3><h4>Ugly way</h4><p>Ok, the simplest way — to run many <em>setTimeout&#39;s</em>, like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FVkmaCdDDeU9eMhNtd_pCA.png" /></figure><p>OK, not so bad, but what if I want to show more elements in different delays?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_aM-J9WdnCjrJbP3VAQ0pg.png" /></figure><p>Looks oldschool and ugly. Can we do better?</p><h4>Ugly way, part 2</h4><p>Lets use RxJS now and try to implement it:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tib9DxYqHvMl2FXCK_WPEw.png" /></figure><p>This code doesn&#39;t allow absolute delay (when the next emission is delayed against start/subscription moment).</p><p>To implement absolute delays approach (all delays are specified against subscription/start moment) we can use <a href="https://rxjs.dev/api/operators/mergeMap"><strong><em>mergeMap</em></strong></a> instead of <a href="https://rxjs.dev/api/operators/concatMap"><strong><em>concatMap</em></strong></a>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rz_4cWxC16LU7EqrEN1kGg.png" /></figure><p>Looks like a lot of code, and also delays are hardcoded. We can do better. So let&#39;s go further!</p><h4>Editor choice method</h4><p>Ok, we already know that hardcoding values/delays is not reusable, and we also want to be able to schedule in <em>relative</em> (delayed against previous emissions) and <em>non-relative/absolute </em>(delayed against subscription moment) way. Taking all the above into account, I implemented a custom <strong>timeRange </strong>function.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PUxyK_cCYZO50x2ZIy1wUA.png" /><figcaption>Custom <strong>timeRange</strong> function.</figcaption></figure><p><strong>Benefits:</strong></p><ol><li>Looks nice and compact.</li><li>Allows both absolute and relative delays to be specified.</li></ol><p>OK, now let&#39;s show three use-cases I mentioned previously.</p><h3><em>Where to use it</em></h3><p><strong>#1 Splash-message control example</strong></p><p>Here it implementation code:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LgFQcOH27DNM1x4UTYIxqw.png" /></figure><p>What do we do here?</p><ol><li>Emit two values with 1000 and 2000 delay (values don&#39;t play any role here, actually only timings matter)</li><li>The first value will show splash element with a message.</li><li>Second will hide it (using <em>.classList.toggle(&#39;show&#39;)</em>)</li></ol><p>Here is how it works:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*2jn_yYTMGcpyLqeDxAG3CA.gif" /></figure><p>You can play with it in a codepen <a href="https://codepen.io/kievsash/pen/ZEzVWKa?editors=0010">here</a>.</p><p><strong>#2 Animating a few elements subsequent appearance</strong></p><p>Here is the code:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JIRhdrg4jCWsR7G_r6BQkQ.png" /></figure><p>We just grab a set of elements and then show them one by one with specified timeouts on each range$ Observable emission.</p><p>Here is the result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*2klU0Bnmad1gZWWD3U20Wg.gif" /></figure><p>You can check the code <a href="https://codepen.io/kievsash/pen/YzKdqzd?editors=0110">here</a>.</p><p><strong>#3 Mocking input params for unit tests code with timeRange.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zE7VK8Cvu3HBxxUJmfETiQ.png" /></figure><p>How it works:</p><ol><li>We mock ajax.get with a function that will return value &#39;42&#39; asynchronously (emulation of network request)</li><li>Then se debounce time = 15ms.</li><li>After that call <em>getSearchResult</em> with mocked <em>input$</em> as a param and subscribe (to start the process).</li><li><em>input$ </em>will emit three values with delays 1ms, 5ms, 30ms. Since debounceTime is set to 15ms, only second and third values will cause network request to be called (ajax.get in our case);</li></ol><p>And result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6cEnFwnKvIVv7tm9jiqGPw.png" /></figure><p>The code and the test can be found <a href="https://codepen.io/kievsash/pen/ePoRxg?editors=0010">here</a>.</p><figure><a href="http://eepurl.com/gHF0av"><img alt="" src="https://cdn-images-1.medium.com/max/594/1*33AyiLH06Tzu-9KxL1hZjg.png" /></a></figure><h4>Conclusion</h4><p>I&#39;ve added <strong><em>timeRange</em></strong> function to my <a href="https://github.com/kievsash/rxjs-toolbox"><strong><em>rxjs-toolbox</em></strong> </a>lib. It is not big for now. You can read more about it here: <a href="https://blog.angularindepth.com/rxjs-recipes-forkjoin-with-the-progress-of-completion-for-bulk-network-requests-in-angular-5d585a77cce1"><em>‘forkJoin’ with the progress of completion</em></a></p><p>Have your own interesting RxJS solutions? Mention them in comments!</p><p>Did you like this article? Then<a href="https://clicktotweet.com/fEdKA"><strong> tweet about it</strong></a><strong> </strong>and<strong> </strong>follow me on <a href="https://twitter.com/El_Extremal"><strong>Twitter</strong></a><strong> </strong>🤓<strong>!</strong></p><p>Want to learn more about RxJS? Give a try to my video-course &quot;<a href="https://www.udemy.com/course/hands-on-rxjs-for-web-development/">Hands-on RxJS</a>&quot;. It contains both total beginners content and more advanced topics as well.</p><p>Cheers.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd6dd07974ce" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/timerange-rxjs-custom-function-that-emits-a-set-of-values-in-specified-timeouts-bd6dd07974ce">TimeRange — RxJS custom function that emits a set of values in specified timeouts</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>