<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Piotr Lewandowski</title>
    <description>The latest articles on DEV Community by Piotr Lewandowski (@constjs).</description>
    <link>https://dev.to/constjs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F8780%2F290300.jpeg</url>
      <title>DEV Community: Piotr Lewandowski</title>
      <link>https://dev.to/constjs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/constjs"/>
    <language>en</language>
    <item>
      <title>Angular Testing: Avoid done() function</title>
      <dc:creator>Piotr Lewandowski</dc:creator>
      <pubDate>Fri, 09 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/constjs/angular-testing-avoid-done-function-1flh</link>
      <guid>https://dev.to/constjs/angular-testing-avoid-done-function-1flh</guid>
      <description>&lt;h4&gt;
  
  
  Let’s talk about harmfulness of real asynchronicity in tests.
&lt;/h4&gt;

&lt;p&gt;Have you ever encountered random test instability on Continuous Integration? Called some test were just “flaky”? Tests that took 10s instead 10ms? I guess you did! There might be lots of reasons for flakiness of tests. I found asynchronous operations are great contributor to &lt;a href="https://engineering.fb.com/2020/12/10/developer-tools/probabilistic-flakiness/" rel="noopener noreferrer"&gt;flakiness score&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here &lt;strong&gt;I want to describe mocking async as simple alternative to&lt;/strong&gt; &lt;strong&gt;done()&lt;/strong&gt; that could avoid many potential build failures.&lt;/p&gt;

&lt;p&gt;I’m going to use Observable to simulate asynchronous operations. It is not limited to RxJs though. The article applies to any kind of asynchronous operations under the hood of components and services.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to check the value produced from Observable, Promise, or callback?
&lt;/h3&gt;

&lt;p&gt;To access variables in the callback, we have to be in its function scope!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('should be green', () =&amp;gt; {
  anyObservable()
    **.subscribe((el) =&amp;gt; {  
 expect(el).toBeTruthy();  
 });**  
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks innocent, sometimes even works! When it does not work? Simply when anyObservable goes async and calls subscribe() with small delay.&lt;/p&gt;

&lt;p&gt;In above example, test is always green then, because test executes faster than subscribe() callback is called. It’s also green when the value does not match expect().&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s simply never checked. Just like &lt;a href="https://en.wikipedia.org/wiki/Volkswagen_emissions_scandal" rel="noopener noreferrer"&gt;volkswagen engines emissions test&lt;/a&gt; — always green.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  When we’re handling asynchronous operation?
&lt;/h4&gt;

&lt;p&gt;Think of any DOM event listeners, HTTP calls, Websockets, animations, own state management events, timers, intervals, Promises and more.&lt;/p&gt;

&lt;p&gt;We do lots of async things in our components. It would be unwise if we just assume those things does not affect tests.&lt;/p&gt;

&lt;p&gt;To overcome this, frameworks like Jest or Karma provide done() function. It’s a marker for test runners not to finish the test until we call it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('should be green for async operation', ( **done** ) =&amp;gt; {
  timeout(500)
    .subscribe((el) =&amp;gt; {
      expect(el).toBeTruthy();
      **done();**  
});
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bingo, isn’t it? So, why do I have the intention to discourage using done()?&lt;/p&gt;

&lt;h3&gt;
  
  
  Poor assumptions of done()
&lt;/h3&gt;

&lt;p&gt;The example above seems to be correct, but it only works under very specific circumstances. There are some common false assumptions of what the done() function does that lead to this confusion.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;🚀 When Observable emits 1000x times&lt;/strong&gt; in a loop by mistake = test is green&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;😩 When Observable emits 2x&lt;/strong&gt; , but the second time it does something different than we expect = test is green&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🛑 When Observable errors&lt;/strong&gt; after first emit = test is green&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⌛️ When Observable never emits&lt;/strong&gt; = test timeouts = slow unit test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏁 When Observable does complete before first emit&lt;/strong&gt; = test timeouts = slow unit test&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;and more…&lt;/p&gt;

&lt;p&gt;As you see, even when some situation goes wrong, test is green. When we use done() in callback, we’re not precise. Those are examples of real bugs we found in tests, not a theoretical mumbo jumbo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do we always need to use done() in callback?
&lt;/h3&gt;

&lt;p&gt;When callbacks are &lt;strong&gt;synchronous&lt;/strong&gt; , we don’t really need to use expect() inside callback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('should be green for sync', () =&amp;gt; {
  // given
  **const result = [];**

  // when
  of(1, 2)
    .subscribe((el) =&amp;gt; **result.push(el)**);

  // then
  **_expect_(result).toEqual([1, 2]);**
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;✅ &lt;strong&gt;When Observable emits 1000x times&lt;/strong&gt; in loop by mistake = test fails &lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;When Observable emits 2x&lt;/strong&gt;, but second times it does something different than we expect = test fails
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;When Observable errors&lt;/strong&gt; after first emit = test fails &lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;When Observable never emits&lt;/strong&gt; = test fails&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;When Observable does complete before first emit&lt;/strong&gt; = test fails&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wouldn’t it be beautiful if we could just skip asynchronous nature of the events?&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1363096314297409537-731" src="https://platform.twitter.com/embed/Tweet.html?id=1363096314297409537"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1363096314297409537-731');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1363096314297409537&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  How to mock async operations? fakeAsync()
&lt;/h3&gt;

&lt;p&gt;Testing asynchronous code is the more typical. Asynchronous tests can be painful. The best way to handle them? Avoid!&lt;/p&gt;

&lt;p&gt;Asynchronous is a side effect, same as a system time clock. We need to avoid them if we want to have a stable and robust test suite.&lt;/p&gt;

&lt;p&gt;In Angular, we have absolute genius mock. It makes everything synchronous and controlled from the tests — fakeAsync().&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('should be green for async', **fakeAsync** (() =&amp;gt; {
  // given
  const result = [];

  // when
  **interval(1000)**.subscribe((el) =&amp;gt; result.push(el));
  **tick(2000)**;

  // then
  expect(result).toEqual([0, 1]);
}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;☝️ Above, we have an interval(1000)emitting new increment every second starting from 0. Typically, &lt;strong&gt;we don’t want to wait real 2 seconds&lt;/strong&gt; to check conditions. For 10 000 tests it means 5 hours of waiting.&lt;/p&gt;

&lt;p&gt;Time is frozen. We’re in charge with tick() function. Whenever we want. Whatever amount of time should pass. With precision to millisecond.&lt;/p&gt;

&lt;p&gt;Again, everything is synchronous. You just don’t need done() function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional advantages of using fakeAsync()
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;We won’t forget done() when we don’t use it&lt;/li&gt;
&lt;li&gt;Test flow is clear and static— expect() always at the end, always executing&lt;/li&gt;
&lt;li&gt;We’re sure we test exactly one async behavior at the time&lt;/li&gt;
&lt;li&gt;We won’t make test utterly slow by using real async operations — think of setTimeout for 5 seconds.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Angular with Ivy — Build performance review</title>
      <dc:creator>Piotr Lewandowski</dc:creator>
      <pubDate>Tue, 14 Apr 2020 19:58:23 +0000</pubDate>
      <link>https://dev.to/constjs/angular-with-ivy-build-performance-review-5ge4</link>
      <guid>https://dev.to/constjs/angular-with-ivy-build-performance-review-5ge4</guid>
      <description>&lt;h4&gt;
  
  
  We have seen various benchmarks and charts on conferences. What is the real impact on our application?
&lt;/h4&gt;

&lt;p&gt;In this article, we’re going to dive into bundle-size and compilation speed insights.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Application context&lt;/li&gt;
&lt;li&gt;
Bundle size benchmark 

&lt;ul&gt;
&lt;li&gt;
Full build size &lt;/li&gt;
&lt;li&gt;gzipped value does not knock down&lt;/li&gt;
&lt;li&gt;
Why is main.js bigger? &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Compilation speed benchmark

&lt;ul&gt;
&lt;li&gt;
Production build &lt;/li&gt;
&lt;li&gt;
Development build &lt;/li&gt;
&lt;li&gt;Recompile time&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;YES!&lt;/li&gt;
&lt;li&gt;Unit testing speed&lt;/li&gt;
&lt;li&gt;Closing thougts&lt;/li&gt;
&lt;li&gt;Bonus: Lesson learned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Discussing how Ivy works is out of our scope. I recommend &lt;a href="https://indepth.dev/ivy-engine-in-angular-first-in-depth-look-at-compilation-runtime-and-change-detection/"&gt;inDepth article about its internals&lt;/a&gt;, for other changes read official blogposts for &lt;a href="https://blog.angular.io/version-9-of-angular-now-available-project-ivy-has-arrived-23c97b63cfa3"&gt;Angular 9&lt;/a&gt; and &lt;a href="https://blog.angular.io/version-9-1-of-angular-now-available-typescript-3-8-faster-builds-and-more-eb292f989428"&gt;Angular 9.1&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application context
&lt;/h3&gt;

&lt;p&gt;Each application is unique. Hence, Ivy enablement is going to cause different improvements. Because of this, benchmark I created must be spoiled by application we develop. Here are few details about it:&lt;/p&gt;

&lt;p&gt;A single &lt;a href="https://nx.dev/angular"&gt;Nx monorepo&lt;/a&gt; with two apps: Bigger main product and smaller support app, both sharing codebase. We develop this codebase for 2 years.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;130k lines of code (TypeScript + HTML)&lt;/li&gt;
&lt;li&gt;800 components&lt;/li&gt;
&lt;li&gt;140+ lazy-loaded modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I conducted tests on Angular 9.0.6 with Ivy: enabled and disabled. I compare ES5 and ES2015 bundles where it makes sense. Bundle size is calculated by a &lt;a href="https://www.npmjs.com/package/webpack-bundle-analyzer"&gt;webpack bundle analyser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the point of writing, we already use Angular 9.1, I’ll put a note where it makes a difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bundle size benchmark
&lt;/h3&gt;

&lt;p&gt;Measurements are based on gzipped vs non-gzipped configuration. To make results easier to analyse, I focus on ES5 builds only.&lt;/p&gt;

&lt;p&gt;Angular team suggest expected values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small applications: 30% improvement&lt;/li&gt;
&lt;li&gt;Medium applications: 2% improvement&lt;/li&gt;
&lt;li&gt;Big applications: 25–45% improvement&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Full build size
&lt;/h4&gt;

&lt;p&gt;I’m using full build metric for the general sum of improvements delivered by the new compiler.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HUisWQLq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AA6AJvl-uP-fokP8KvxCn_Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HUisWQLq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AA6AJvl-uP-fokP8KvxCn_Q.png" alt=""&gt;&lt;/a&gt;Full production bundle size Values are Megabytes.&lt;/p&gt;

&lt;h4&gt;
  
  
  gzipped value does not knock down
&lt;/h4&gt;

&lt;p&gt;Full build metrics does not relief what is going on under the hood. List of files (for us its over 140) splits on main.js file that is loaded as first and 139 lazy loaded modules.&lt;/p&gt;

&lt;p&gt;Let’s split comparison:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MHwUInLS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A0Cj7yzret3T-yNwKgn03Pw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MHwUInLS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A0Cj7yzret3T-yNwKgn03Pw.png" alt=""&gt;&lt;/a&gt;Comparison of main.js and sum of lazy loaded chunks. Values are Megabytes.&lt;/p&gt;

&lt;h4&gt;
  
  
  🤯 Why is main.js bigger?
&lt;/h4&gt;

&lt;p&gt;Lazy loaded chunks are purely compiled with Ivy, effecting smaller output of gzipped and minified bundles by 20–30%!&lt;/p&gt;

&lt;p&gt;This is good, but what is going on with main.js? It meant to be much smaller because of better tree-shaking of Angular!&lt;/p&gt;

&lt;p&gt;Have a look. main.js consist of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular libraries,&lt;/li&gt;
&lt;li&gt;utility libraries,&lt;/li&gt;
&lt;li&gt;components / services that couldn’t be lazy loaded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Utility libraries have nothing to do with Ivy, the same with Angular Services. What left us: Angular framework and component library.&lt;/p&gt;

&lt;p&gt;Let’s go even deeper and compare only those:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--USJ0Gh-b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A3WmxyHoaAF9RKrsQPnA4Mg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--USJ0Gh-b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A3WmxyHoaAF9RKrsQPnA4Mg.png" alt=""&gt;&lt;/a&gt;Angular library (left), our in-house &lt;a href="https://barista.dynatrace.com/components"&gt;barista components library&lt;/a&gt;. Values are Kilobytes.&lt;/p&gt;

&lt;p&gt;That solves a riddle. For Angular 9, each Angular library — framework itself, component libraries, NgRx — needs to be re-compiled now with ngcc tool to run with Ivy runtime. ngcc re-compiles node_modules libs with Ivy compiler. This process might make components little bigger.&lt;/p&gt;

&lt;p&gt;Other cause of bigger size I noticed is, with Ivy enabled, much less lazy loaded chunks are generated. For Pre-Ivy bundle we had 143 generated JS files, for Ivy its only 37 files — split by only root lazy-loaded modules. More code lands into main.js too.&lt;/p&gt;

&lt;p&gt;This could be related to a breaking change described in &lt;a href="https://angular.io/guide/ivy-compatibility#payload-size-debugging"&gt;Ivy compatibility guide&lt;/a&gt; to be fixed by libraries we use, we had suggestion applied in place though without effect.&lt;/p&gt;

&lt;p&gt;Is it bad? Well, on chart — maybe. But it’s temporary situation. Initial plan for Angular 10 is to work without ngcc. I believe, once migration period is over those values are going to be much smaller. Here is my reasoning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Currently libraries are compiled in JIT-mode, then re-compiled with ngcc&lt;/li&gt;
&lt;li&gt;Angular 10: libraries are going to publish code compiled by Ivy with AOT&lt;/li&gt;
&lt;li&gt;Then, we won’t need compatibility layers for components with ngcc&lt;/li&gt;
&lt;li&gt;AOT-compiled output is additional bonus, making libraries even smaller&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Compilation speed benchmark
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Production build
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xMmsMPzX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AL-eq04ysnLRghuqnAaBQGQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xMmsMPzX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AL-eq04ysnLRghuqnAaBQGQ.png" alt=""&gt;&lt;/a&gt;Production build time in minutes.&lt;/p&gt;

&lt;p&gt;Differential loading (ES2015+ES5) build used to be slower for obvious reasons, it needed to generate two packages of the same code. Ivy helps with both builds, saving 1 minute of build time — that’s a nice improvement. Moreover, it reduces gap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 instead of 6 minutes for differential loading&lt;/li&gt;
&lt;li&gt;3 minutes 40 seconds instead 4 minutes 45 seconds for regular ES5 build&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎉 20–30% improvement!&lt;/p&gt;

&lt;h4&gt;
  
  
  Development build
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7TL6nRQS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AL3MACuwTHbj-lQiAIVBFCA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7TL6nRQS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AL3MACuwTHbj-lQiAIVBFCA.png" alt=""&gt;&lt;/a&gt;Development build time in minutes.&lt;/p&gt;

&lt;p&gt;For development build, there is no differential loading. We compile ES2015 or ES5, never both. Here ES2015 is proven to be 10–20s faster.&lt;/p&gt;

&lt;p&gt;After enabling Ivy, we see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ES2015: 1min 25s instead 1min 55s&lt;/li&gt;
&lt;li&gt;ES5: 1min 40s instead 2min&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎉 20–25% improvement! 30 seconds gained for every build started 😎&lt;/p&gt;

&lt;h4&gt;
  
  
  Recompile time
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qgWjmji---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AhjyMCr6vvw3qnZaf5_vCaA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qgWjmji---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AhjyMCr6vvw3qnZaf5_vCaA.png" alt=""&gt;&lt;/a&gt;change re-compilation time (minutes)&lt;/p&gt;

&lt;p&gt;Re-compilation time depends heavily on the place you’re editing. Smaller, lazy-loaded modules are re-compiling faster than modules inside main.js.&lt;/p&gt;

&lt;p&gt;We’ve already observed significant improvements in recompilation times after upgrade to Angular 8.2 (mostly thanks to TypeScript improvements), but yay! Ivy compiler managed to squeeze times even more.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small, lazy-loaded module: 5–7 seconds instead of 10–12s&lt;/li&gt;
&lt;li&gt;Change in HTML in core components: 12–13s instead of 20s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎉 30–40% improvement! 😲 5s every-change makes a dramatic difference in development experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  YES!
&lt;/h3&gt;

&lt;p&gt;Those results are worth waiting for, especially we’re at the process of migration to full Ivy potential, and we can expect further improvements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BUT!&lt;/strong&gt; I mentioned libraries, those need to be re-compiled with ngcc tool before a build. For us it costs &lt;strong&gt;40–50s for Angular 9.0&lt;/strong&gt; and &lt;strong&gt;20–30s for Angular 9.1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Happily, in Angular 9.1, ngcc is smart enough to run on-demand, not every compilation and not every post-install change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Dr8GfHO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AKwwOfH599wSlDFXfRIDTUA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Dr8GfHO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AKwwOfH599wSlDFXfRIDTUA.png" alt=""&gt;&lt;/a&gt;development build time of first run (minutes)&lt;/p&gt;

&lt;p&gt;Overhead is going to be visible only on first run after adding/changing dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit testing speed
&lt;/h3&gt;

&lt;p&gt;We have a new TestBed implementation that allows to cache component definitions without the need to re-compile every test run. It’s expected to speed-up by 25–50%.&lt;/p&gt;

&lt;p&gt;I cannot comment it as we use jest with &lt;a href="https://github.com/thymikee/jest-preset-angular"&gt;preset for angular configuration&lt;/a&gt;,&lt;br&gt;&lt;br&gt;
I haven’t seen any difference for our set-up just by enabling/disabling Ivy.&lt;/p&gt;
&lt;h3&gt;
  
  
  Closing thoughts
&lt;/h3&gt;

&lt;p&gt;You might get impression that results are not worth upgrade.&lt;br&gt;&lt;br&gt;
No! Nothing like that. ngcc adds some overhead both size and speed, but:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8pxatZ42--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AD10joa371AhyDXkmZhS70Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8pxatZ42--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AD10joa371AhyDXkmZhS70Q.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After having a thought, I’m happy bundle size is just not worse during migration period. We already gained build time benefits.&lt;/p&gt;

&lt;p&gt;I’m thankful for ngcc, so library authors can have versions compatible with Angular 8 and Angular 9.&lt;/p&gt;

&lt;p&gt;I’m thankful we’re not going to split community into Post-Ivy and Pre-Ivy era, the way Python 2 vs Python 3 or Angular 1 vs Angular 2+ did. 😉&lt;/p&gt;

&lt;p&gt;In the next blogpost, I’ll present a few tips on migrating to Angular 9 and 9.1 with a single-file commit. Wait, is it even possible with all breaking changes? Yes! But you need to act smart.&lt;/p&gt;
&lt;h3&gt;
  
  
  Bonus: Lesson learned
&lt;/h3&gt;

&lt;p&gt;Don’t expect Ivy project to speed-up much when you have bugs in your build configuration!&lt;/p&gt;

&lt;p&gt;The front-end toolset is complex. Even minor dependency might grow your bundle or compilation time. Thus, make sure you’re doing bundle analysis occasionally (preferably, on a regular basis!).&lt;/p&gt;

&lt;p&gt;Angular team tries to save every bit of code in runtime, whereas e.g. sass-loader might add megabytes of duplicated CSS just because of a simple mistake in the configuration.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--RwZA_V-Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/988006718729793536/gjcogKs2_normal.jpg" alt="Piotr Lewandowski profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Piotr Lewandowski
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/constjs"&gt;@constjs&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      🚀 technology radar&lt;br&gt;&lt;br&gt;Just recently 50% of our build time was took by sass-loader plugin. We figured out bug after 1 month of slow-down.&lt;br&gt;&lt;br&gt;This plugin has potential to save hours of debugging and days of development time 🔥 &lt;a href="https://t.co/gFKYkwCu16"&gt;twitter.com/addyosmani/sta…&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      19:02 PM - 07 Mar 2020
    &lt;/div&gt;

      &lt;div class="ltag__twitter-tweet__quote"&gt;
        &lt;div class="ltag__twitter-tweet__quote__header"&gt;
          &lt;span class="ltag__twitter-tweet__quote__header__name"&gt;
            Addy Osmani
          &lt;/span&gt;
          &lt;a class="comment-mentioned-user" href="https://dev.to/addyosmani"&gt;@addyosmani&lt;/a&gt;

        &lt;/div&gt;
        Use webpack? the Speed Measure Plugin measures speed of your build times, plugins and loaders: https://t.co/nTO7OPt3gT ~ see where it's worth optimizing. https://t.co/2wIlVYTyte
      &lt;/div&gt;

    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1236366698430828546" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1236366698430828546" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1236366698430828546" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;What about your projects? Did you perform similar benchmarks? I’d be happy to hear about your results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you’ve learned something new, please:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ use ❤️, 🦄, 👏 button&lt;/strong&gt; so more people can see this&lt;br&gt;&lt;br&gt;
&lt;strong&gt;→&lt;/strong&gt; &lt;a href="https://twitter.com/constjs"&gt;&lt;strong&gt;follow me&lt;/strong&gt; on Twitter (@constjs)&lt;/a&gt; so you won’t miss future posts:&lt;/p&gt;




</description>
      <category>angular</category>
      <category>ivy</category>
      <category>webpack</category>
      <category>performance</category>
    </item>
    <item>
      <title>Angular - Translate Enums (i18n)</title>
      <dc:creator>Piotr Lewandowski</dc:creator>
      <pubDate>Mon, 08 Jul 2019 12:19:05 +0000</pubDate>
      <link>https://dev.to/constjs/angular-translate-enums-i18n-57k3</link>
      <guid>https://dev.to/constjs/angular-translate-enums-i18n-57k3</guid>
      <description>&lt;p&gt;Built-in Angular translation engine supports (so-far) only translation within templates, so you might think it's not possible to translate parts of TypeScript like enums. &lt;/p&gt;

&lt;p&gt;In fact, it's quite easy if you follow one practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model from server
&lt;/h2&gt;

&lt;p&gt;To make things easy to reason about, let's have a Todo App :)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;TODO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TODO&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IN_PROGRESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IN_PROGRESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DONE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;ul&amp;gt;
      &amp;lt;li *ngFor="let item of items"&amp;gt;
      {{ item.name }} ({{ item.state }})
      &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TodoList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what is the problem?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;{{ items.state }}&lt;/code&gt; will produce generated enums values (0, 1, 2... or 'TODO', 'IN_PROGRESS'...)&lt;/li&gt;
&lt;li&gt;We need to convert enum value into string, however this has to be within template, not TypeScript&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Bad example
&lt;/h3&gt;

&lt;p&gt;Often we tend to create method with switch-case, which is unfortunate because Angular i18n is not aware of those strings, and so - it won't touch them during translation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Don't do it&lt;/span&gt;
&lt;span class="nx"&gt;getStateMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IN_PROGRES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DONE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finished&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to make it translatable?
&lt;/h2&gt;

&lt;p&gt;There is only one rule to follow:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every string visible in UI has to be put in template&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Usually in our team, for complex string calculation (enums, or some text logic) we create &lt;em&gt;new component responsible only for translation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We're using it widely in our applications, making a clear distinction between screen-logic and text-logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution #1
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-state-i18n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;ng-container [ngSwitch]="key"&amp;gt;
    &amp;lt;ng-container i18n *ngSwitchCase="todoState.TODO"&amp;gt;not started&amp;lt;/ng-container&amp;gt;
    &amp;lt;ng-container i18n *ngSwitchCase="todoState.IN_PROGRESS"&amp;gt;started&amp;lt;/ng-container&amp;gt;
    &amp;lt;ng-container i18n *ngSwitchCase="todoState.DONE"&amp;gt;finished&amp;lt;/ng-container&amp;gt;
    &amp;lt;ng-container i18n *ngSwitchDefault&amp;gt;not defined&amp;lt;/ng-container&amp;gt;
  &amp;lt;/ng-container&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TodoStateI18n&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// enum has to be accessed through class field&lt;/span&gt;
  &lt;span class="nx"&gt;todoState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And final usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;ul&amp;gt;
      &amp;lt;li *ngFor="let item of items"&amp;gt;
      {{ item.name }} (&amp;lt;todo-state-i18n key="item.state"&amp;gt;&amp;lt;/todo-state-i18n&amp;gt;)
      &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TodoList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This works only with regular enums, &lt;code&gt;const enum&lt;/code&gt; cannot be used within template (at least, not out of the box)&lt;/li&gt;
&lt;li&gt;We happily use this practice not only for enums, but also for string manipulations.&lt;/li&gt;
&lt;li&gt;You still need to remember to update template when new enum values are added (e.g. &lt;code&gt;TodoState.BLOCKED&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solution #2 - ICU messages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-state-i18n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;ng-container i18n&amp;gt;
    {key, select,
      TODO {not started}
      IN_PROGRESS {started}
      DONE {finished}
    }
  &amp;lt;/ng-container&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TodoStateI18n&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Works with const enums&lt;/li&gt;
&lt;li&gt;Useful especially for string enums&lt;/li&gt;
&lt;li&gt;Simpler approach, but also supports HTML elements e.g. &lt;code&gt;TODO {&amp;lt;span&amp;gt;not&amp;lt;/span&amp;gt; started}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;To be secure, you need to write unit tests that checks enum values&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>i18n</category>
    </item>
  </channel>
</rss>
