<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Frederik Prijck</title>
    <description>The latest articles on Forem by Frederik Prijck (@frederikprijck).</description>
    <link>https://forem.com/frederikprijck</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%2F231562%2Fe97fa6c7-cd7f-4185-94b7-ff9edc151a60.png</url>
      <title>Forem: Frederik Prijck</title>
      <link>https://forem.com/frederikprijck</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/frederikprijck"/>
    <language>en</language>
    <item>
      <title>Reusable HTML in Angular using NgTemplateOutlet</title>
      <dc:creator>Frederik Prijck</dc:creator>
      <pubDate>Fri, 14 Feb 2020 22:16:48 +0000</pubDate>
      <link>https://forem.com/frederikprijck/reusable-html-in-angular-using-ngtemplateoutlet-3l8p</link>
      <guid>https://forem.com/frederikprijck/reusable-html-in-angular-using-ngtemplateoutlet-3l8p</guid>
      <description>&lt;p&gt;When creating an application, you might find yourself reusing the same HTML. One way to get rid of this could be to make use of components. Even tho I love using components for this, Angular allows us to reuse HTML without introducing components as well.&lt;/p&gt;

&lt;p&gt;Recently I find myself using this as I wanted to limit the amount of refactoring in the current PR (so I didn't want to move parts of the HTML and code into a component) while I still wanted to introduce reusability in the HTML.&lt;/p&gt;

&lt;p&gt;Take a look at the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Movies&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let movie of movies"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ movie.name }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ movie.description }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onClick(movie)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Favorite Movies&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let movie of favoriteMovies"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ movie.name }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ movie.description }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onClick(movie)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As mentioned, to limit duplication and introduce reusability we could move the content of our &lt;code&gt;ngFor&lt;/code&gt; into its own component, add the appropriate input and outputs and set up the bindings appropriately.&lt;/p&gt;

&lt;p&gt;Apart from using a Component, we can also make use of Angular's NgTemplateOutlet. I'll generally recommend using a component. However, if you find your self in a situation where the HTML you wanna reuse is so big and bound to the current component in so many ways, using NgTemplateOutlet might be a first step into making the HTML reusable. Another reason to use NgTemplateOutlet could also be when the HTML is so simple, that introducing a separate component might not be making things easier to grasp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;#itemTemplate&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ item.name }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ item.description }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onClick(item)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moving the content of our &lt;code&gt;ngFor&lt;/code&gt; into an &lt;code&gt;ng-template&lt;/code&gt; is pretty straightforward. However, as we want to render this template several times, each time for a different item, we'll need to declare some kind of context for the ng-template so that angular knows how to bind the data. In our case, we'll be adding &lt;code&gt;let-item&lt;/code&gt; to the ng-template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;#itemTemplate&lt;/span&gt; &lt;span class="na"&gt;let-item&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ item.name }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ item.description }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onClick(item)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this template can be done by using the &lt;code&gt;ngTemplateOutlet&lt;/code&gt; directive on an &lt;code&gt;ng-container&lt;/code&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutlet]=&lt;/span&gt;&lt;span class="s"&gt;"itemTemplate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add a context object to ngTemplateOutlet which allows us to provide it with a value to use as the item we have defined on our ng-template.&lt;/p&gt;

&lt;p&gt;A context object should be an object, the object's keys will be available for binding by the local template let declarations. Using the key $implicit in the context object will set its value as default.&lt;/p&gt;

&lt;p&gt;What this means is that we can either use &lt;code&gt;let-item="movie"&lt;/code&gt; and provide the context as an object containing a &lt;code&gt;movie&lt;/code&gt; property: &lt;code&gt;[ngTemplateOutletContext]="{ movie: movie }"&lt;/code&gt; or use &lt;code&gt;let-item&lt;/code&gt; and &lt;code&gt;[ngTemplateOutletContext]="{ $implicit: movie }"&lt;/code&gt;. Both of them will make the movie available as  &lt;code&gt;item&lt;/code&gt; inside the template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutlet]=&lt;/span&gt;&lt;span class="s"&gt;"itemTemplate"&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutletContext]=&lt;/span&gt;&lt;span class="s"&gt;"{ $implicit: movie }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting everything together, we now should be able to refactor our HTML to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Movies&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let movie of movies"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutlet]=&lt;/span&gt;&lt;span class="s"&gt;"itemTemplate"&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutletContext]=&lt;/span&gt;&lt;span class="s"&gt;"{ $implicit: movie }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Favorite Movies&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let movie of favoriteMovies"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutlet]=&lt;/span&gt;&lt;span class="s"&gt;"itemTemplate"&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutletContext]=&lt;/span&gt;&lt;span class="s"&gt;"{ $implicit: movie }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;let-item&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ item.name }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ item.description }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onClick(item)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even add another ng-template to reuse the list. For demonstration purposes, we'll not use the &lt;code&gt;$implicit&lt;/code&gt; property on the NgTemplateOutlet's context in this case. Instead, we'll explicitly assign our value to a property, other than $implicit, and use that same property in &lt;code&gt;let-list="list"&lt;/code&gt;, where the right side of the assignment refers to the context property while the left side is defining a variable usable in the ng-template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Movies&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutlet]=&lt;/span&gt;&lt;span class="s"&gt;"listTemplate"&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutletContext]=&lt;/span&gt;&lt;span class="s"&gt;"{ list: movies }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Favorite Movies&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutlet]=&lt;/span&gt;&lt;span class="s"&gt;"listTemplate"&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutletContext]=&lt;/span&gt;&lt;span class="s"&gt;"{ list: favoriteMovies }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;#listTemplate&lt;/span&gt; &lt;span class="na"&gt;let-list=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let item of list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutlet]=&lt;/span&gt;&lt;span class="s"&gt;"itemTemplate"&lt;/span&gt; &lt;span class="na"&gt;[ngTemplateOutletContext]=&lt;/span&gt;&lt;span class="s"&gt;"{ $implicit: item }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;#itemTemplate&lt;/span&gt; &lt;span class="na"&gt;let-item&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ item.name }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ item.description }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onClick(item)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The ngTemplateOutlet directive can also be used as a structural directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;*ngTemplateOutlet=&lt;/span&gt;&lt;span class="s"&gt;"itemTemplate; context: { $implicit: item }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Even tho using Components is still the most ideal way in Angular to introduce reusable blocks, Angular's ngTemplateOutlet allows for a way to make use of reusable blocks of HTML without introducing new components.&lt;/p&gt;

&lt;p&gt;You can find a running example at: &lt;a href="https://stackblitz.com/edit/angular-fkqhbz?file=src%2Fapp%2Fapp.component.ts"&gt;https://stackblitz.com/edit/angular-fkqhbz&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Converting a Promise into an Observable</title>
      <dc:creator>Frederik Prijck</dc:creator>
      <pubDate>Mon, 04 Nov 2019 21:34:32 +0000</pubDate>
      <link>https://forem.com/frederikprijck/converting-a-promise-into-an-observable-dag</link>
      <guid>https://forem.com/frederikprijck/converting-a-promise-into-an-observable-dag</guid>
      <description>&lt;p&gt;When working with rxjs, you might find yourself in a situation where you want to integrate a promise in a reactive code base. In order to embrace the full reactivity, it's a good idea to convert that promise into an observable so we can easily pipe other operators or even combine it with other streams.&lt;/p&gt;

&lt;p&gt;Previously, rxjs had an operator that was specifically designed for this use-case: &lt;code&gt;fromPromise&lt;/code&gt;. Current versions of rxjs have dropped &lt;code&gt;fromPromise&lt;/code&gt; in favor of &lt;code&gt;from&lt;/code&gt;, however, there's no real difference in usage. The &lt;code&gt;from&lt;/code&gt; operator, apart from arrays and strings, accepts a promise in order to convert it into an Observable.&lt;/p&gt;

&lt;p&gt;If you are interested in knowing how it handles a promise or how it defines whether or not it's a promise that's being passed in, have a look at &lt;a href="https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/from.ts#L114" rel="noopener noreferrer"&gt;https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/from.ts#L114&lt;/a&gt; and &lt;a href="https://github.com/ReactiveX/rxjs/blob/master/src/internal/util/subscribeTo.ts#L20" rel="noopener noreferrer"&gt;https://github.com/ReactiveX/rxjs/blob/master/src/internal/util/subscribeTo.ts#L20&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code is the promise representation of the snippet that we want to convert to using observables in such a way that we can integrate it with other, existing, observables.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is about a function that's returning a Promise that we'll be converting into an Observable, not just a standalone Promise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Implementing the from operator comes down to wrapping the promise with the from operator and replacing &lt;code&gt;.then(...)&lt;/code&gt; with RXjs' &lt;code&gt;map(...)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should do it, right? We have successfully converted the promise returning function into a function that's returning an Observable. We can now start combining this with other Observables/Operators so that we can create more advanced streams.&lt;/p&gt;

&lt;p&gt;But what if I told you this probably isn't what you want (yet)?&lt;/p&gt;

&lt;h1&gt;
  
  
  Lazy Observables
&lt;/h1&gt;

&lt;p&gt;When using observables, it's not expected that anything happens for as long as there is no active subscription. However, removing the subscription from the above code will still trigger the HTTP call. You can see this in action here: &lt;a href="https://stackblitz.com/edit/rxjs-bb626s" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/rxjs-bb626s&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you would inspect the DevTools' network tab, you will notice that the HTTP call is indeed triggered, even tho we do not have any subscription.&lt;/p&gt;

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

&lt;p&gt;We can solve this by either using an existing rxjs operator, in combination with the &lt;code&gt;from&lt;/code&gt; operator we're already using or you can decide to build the observable from scratch. &lt;/p&gt;

&lt;h2&gt;
  
  
  Using the defer operator
&lt;/h2&gt;

&lt;p&gt;Rxjs' defer operator can be used to wait until an observer subscribes before creating the actual observable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTodo$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;getTodo$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will ensure that the HTTP call is only triggered after 5000ms, which is the moment that we're adding a subscription to the observable.&lt;br&gt;
You can see this in action &lt;a href="https://stackblitz.com/edit/rxjs-fgwokv" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/rxjs-fgwokv&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Most of the time, you might be bringing in asynchronous data as a matter of a mergeMap/switchMap/exhaustMap/concatMap operation, which might be returning an Observable that's originating from a Promise in some cases. If that's the case, we, technically, have no need to use defer as the Observable will not be created until the source Observable emits. However, it's still not a bad idea to use defer either way to ensure the Promise is lazy, no matter how it's used.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Building the observable from scratch
&lt;/h2&gt;

&lt;p&gt;Even tho I'd recommend using existing rxjs operators when possible, I think for converting a Promise to an Observable it's worth taking control over the Observable creation ourselves so that we have more control over what happens when we unsubscribe from the Observable (which we will cover in promise cancellation).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTodo$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;getTodo$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code will create an observable based on the promise and only subscribe to it after 5000 ms. If you would have a look at this stackblitz &lt;a href="https://stackblitz.com/edit/rxjs-4zj1bx" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/rxjs-4zj1bx&lt;/a&gt;, you will see that the HTTP call is only triggered after 5 seconds. So our observable is now lazy in such a way that it will only resolve the promise (and trigger the HTTP call) when a subscription is added.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that we are adding an explicit subscription which we're returning from the Observable's constructor callback as the teardown logic. Doing so ensures that that subscription is cleanup whenever we unsubscribe from the observable returned by &lt;code&gt;getTodo()&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Promise cancellation
&lt;/h2&gt;

&lt;p&gt;We're still missing one crucial part in our Promise to Observable conversion. In our case, the promise was representing an HTTP call. Whenever we unsubscribe from the observable before the HTTP call is finished, we probably want to abort the open HTTP request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;abortController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;abortController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;
        &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;abortController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTodo$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getTodo$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AbortController is a built-in interface that allows us to cancel DOM requests, including Promises. Even though a lot of async operations might require a custom AbortController implementation, the fetch API supports the AbortController by default. This means that all we need to do is create an AbortController instance, pass it's signal property to the fetch method and call abort whenever appropriate, in our case meaning in the TearDownLogic, which is called whenever we unsubscribe from the Observable. You can read more about Aborting a fetch on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a stackblitz containing the functionality to abort the HTTP call: &lt;a href="https://stackblitz.com/edit/rxjs-7wc1rb" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/rxjs-7wc1rb&lt;/a&gt;. If you'd inspect the DevTools' network tab, you'll notice an HTPP call is being triggered but it's instantly canceled.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Rxjs fetch operator
&lt;/h1&gt;

&lt;p&gt;Rxjs has built-in support for converting the fetch API to an observable (see: &lt;a href="https://github.com/ReactiveX/rxjs/blob/0e4849a36338133ac3c1b890cd68817547177f44/src/internal/observable/dom/fetch.ts" rel="noopener noreferrer"&gt;https://github.com/ReactiveX/rxjs/blob/0e4849a36338133ac3c1b890cd68817547177f44/src/internal/observable/dom/fetch.ts&lt;/a&gt;&lt;br&gt;
). As you might notice, it is also using the AbortController to cancel the HTTP call when unsubscribed from the Observable (even tho it's slightly more complicated because this article sticks to the basics of promise cancelation). You probably want to be using that instead of hard-crafting your own. However, this article is intended to give you an example on how we can convert any promise to an Observable.&lt;/p&gt;

</description>
      <category>rxjs</category>
      <category>promises</category>
      <category>observables</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Continuously Deploying Angular to Azure Storage with Azure DevOps</title>
      <dc:creator>Frederik Prijck</dc:creator>
      <pubDate>Wed, 30 Oct 2019 21:57:55 +0000</pubDate>
      <link>https://forem.com/thisdotmedia/continuously-deploying-angular-to-azure-storage-with-azure-devops-3983</link>
      <guid>https://forem.com/thisdotmedia/continuously-deploying-angular-to-azure-storage-with-azure-devops-3983</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In a previous &lt;a href="https://dev.to/thisdotmedia/continuously-deploying-angular-to-azure-app-service-with-azure-devops-4hf2"&gt;article&lt;/a&gt;, we saw how to use Azure DevOps to deploy an Angular application to an Azure App Service running .NET Core.&lt;/p&gt;

&lt;p&gt;Even though an Angular application works perfectly on an Azure App Service, we don't need a .NET runtime (or any other runtime such as nodejs, Ruby, PHP, ...) to host our Angular application if it's not using Server Side Rendering.&lt;/p&gt;

&lt;p&gt;If you're using Server Side Rendering, you do need a runtime such as nodejs to be able to run a process that's responsible for rendering, and serving, the Angular application. In this article, we're deploying an application that doesn't use Server Side Rendering, so we have no need for such a runtime.&lt;/p&gt;

&lt;p&gt;An Azure Storage Account is mostly used for storing blobs, and files, but it also contains a queue, and a NoSQL database with which you can integrate.&lt;/p&gt;

&lt;p&gt;Apart from the above features, Azure Storage Account provides a way to host a static (HTML, CSS, JavaScript, and other assets) site, such as an Angular application. The hosting is free. You're only being billed for the blob storage taken by the files of your application. You can find more details on Azure Storage Blob pricing at &lt;a href="https://azure.microsoft.com/en-us/pricing/details/storage/blobs/" rel="noopener noreferrer"&gt;https://azure.microsoft.com/en-us/pricing/details/storage/blobs/&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating a Storage Account
&lt;/h1&gt;

&lt;p&gt;We can create a Storage Account through the Azure Portal; ensure you have an active subscription to which we can add the Storage Account. You can create one (&lt;a href="https://docs.microsoft.com/bs-latn-ba/azure/billing/billing-mca-create-subscription" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you don't have one yet. Make sure you choose a Free Trial subscription in order to not be charged. A Free Trial shouldn't be used for production). &lt;/p&gt;

&lt;p&gt;Inside the Azure Portal, navigate to All Services, and search for Storage accounts. Selecting Storage accounts from the search results will bring you to the Storage accounts overview. Click "add" to create a new Storage Account, and fill in the required information in the Basics tab:&lt;/p&gt;

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

&lt;p&gt;Select the subscription and resource group in which we want to create the storage account, and provide it with a name (the name has to be unique across all storage accounts in Azure). The other options can be left untouched. You can find more information about these configuration settings at &lt;a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-introduction#types-of-storage-accounts" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/azure/storage/common/storage-introduction#types-of-storage-accounts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We don't need to change anything in the other tabs (Networking, Advanced and Tags). Go ahead and create the Storage Account. Azure should navigate you to the deployment details, showing the progress of the deployment. Once you have deployed, you should be able to click "Go to resource" in order to go to the created Storage Account.&lt;/p&gt;

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

&lt;p&gt;As mentioned before, there are different ways we can make use of an Azure Storage. We're not going to cover the details of any of its features, other than hosting a static website. &lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring a Static Website
&lt;/h3&gt;

&lt;p&gt;You can access the configuration regarding static websites from the Storage Account's side-menu (Settings ⇒ Static website)&lt;/p&gt;

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

&lt;p&gt;As static website hosting is disabled by default, we need to enable it in order to be able to host a static website inside a Storage Account. Once enabled, we need to provide an index document name and, optionally, an error document path. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Feykuu9dgpo2ux7nwz2sf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Feykuu9dgpo2ux7nwz2sf.png" alt="Enable static website for Storage Account"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error document path should point to an HTML file that will be rendered when a request to Azure Storage returns a 404. Currently, we don't have such a file, so let's keep it empty for this article. &lt;/p&gt;

&lt;p&gt;Once saved, you should see a primary and secondary endpoint being listed. These are the URLs on which the application, once deployed, will be available. Apart from the URL, you'll also see that a container, named &lt;strong&gt;$web&lt;/strong&gt;, has been created to host the static website. &lt;/p&gt;

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

&lt;p&gt;As we haven't deployed any files yet, trying to open any of the endpoint URL's in your favorite browser will show the default 404 message (this is the message that would be replaced with an HTML file if we had configured an Error document path).&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Configure a Release Pipeline for a Storage Account
&lt;/h1&gt;

&lt;p&gt;We'll be creating a release pipeline that will use the artifacts published as part of the build pipeline that was configured in &lt;a href="https://dev.to/thisdotmedia/continuously-integrating-angular-with-azure-devops-2k9l"&gt;https://dev.to/thisdotmedia/continuously-integrating-angular-with-azure-devops-2k9l&lt;/a&gt;. This article assumes that the Angular application that was used in the above article has been updated to include environmental configuration as described in &lt;a href="https://dev.to/thisdotmedia/runtime-environment-configuration-with-angular-4f5j"&gt;https://dev.to/thisdotmedia/runtime-environment-configuration-with-angular-4f5j&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When inside an Azure DevOps project, create a new Release Pipeline by selecting Pipeline ⇒ Releases ⇒ New ⇒ New Release Pipeline&lt;/p&gt;

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

&lt;p&gt;We're not going to use a template for our release pipeline, so you can choose to start with an empty job instead of selecting a preconfigured template.&lt;/p&gt;

&lt;p&gt;As Azure DevOps supports multiple stages, you can provide a name for the stage that's created by default, or you can keep Stage 1 as its name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding build artifacts
&lt;/h2&gt;

&lt;p&gt;Let's start by adding the artifacts that we want to deploy to Azure Storage. Click Add an Artifact, select the Build source type (as the artifacts are stored within the build pipeline), and select the appropriate build pipeline from the source dropdown. You can keep the default values for both the version to deploy, as we always want to deploy the latest version, and the source alias. The source alias is the folder that will be used to download the artifacts. We will be using this value throughout the next steps when manipulating, and uploading the artifacts.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Adding deployment tasks
&lt;/h2&gt;

&lt;p&gt;As we have started with an empty job, there are no tasks defined yet. We'll need to add a task to deploy the artifact's files to an Azure Storage Account. To do so, we can make use of the integrated &lt;strong&gt;Azure file copy&lt;/strong&gt; task by going to the tasks section, clicking the '+' on the Agent job, searching for Azure file copy, and clicking add.&lt;/p&gt;

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

&lt;p&gt;When adding an Azure file copy task, you will need to provide the required information in order for Azure DevOps to know what files have to be deployed to which destination. This includes both the Azure Subscription, and the Storage Account information.&lt;/p&gt;

&lt;p&gt;As we're going to deploy the build artifacts, which are configured to be available inside the release definition, we'll need to provide the path to the source that we want to deploy. You can navigate the artifacts, and select the appropriate folder, by clicking the &lt;code&gt;...&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;Once we have configured the files that should be deployed, select your Azure Subscription, select Azure Blob as the Destination Type, choose the appropriate Storage Account that you want to use, and provide &lt;strong&gt;$web&lt;/strong&gt; for the container name (this is the default container name used by Azure DevOps when enabling static websites inside an Azure Storage Account).&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Environment specific configuration
&lt;/h2&gt;

&lt;p&gt;As mentioned in the previous article (&lt;a href="https://dev.to/thisdotmedia/runtime-environment-configuration-with-angular-4f5j"&gt;https://dev.to/thisdotmedia/runtime-environment-configuration-with-angular-4f5j&lt;/a&gt;), we're not making use of the built-in Angular environment system in order for our application to make use of environment-specific configuration.&lt;/p&gt;

&lt;p&gt;As we want to avoid rebuilding the application in order for it to use environment-specific configuration, we're using a JSON file that's served as part of the assets directory, and is retrieved in our Angular application using an Http Request. You can find a detailed overview on how to configure this in the article mentioned above.&lt;/p&gt;

&lt;p&gt;We will need a File transform task to modify the contents of the config.json file as part of our release pipeline's stage (in case of multiple stages, every stage can have its own step, and can replace the contents with different values before deploying). Add the task, and configure it to target the &lt;strong&gt;assets/config.json&lt;/strong&gt; file inside our web-app artifact.&lt;/p&gt;

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

&lt;p&gt;Ensure this task is executed before deploying the files to Azure Storage.&lt;/p&gt;

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

&lt;p&gt;In order for Azure DevOps to know what names and values it has to use while transforming the config file, we will need to create release pipeline variables. The file transform task will use all the variables, and update values based on its name.&lt;/p&gt;

&lt;p&gt;All we need for now is for an apiUrl to be configurable for each environment, so we'll need an apiUrl pipeline variable (I went with &lt;a href="http://localhost/" rel="noopener noreferrer"&gt;localhost&lt;/a&gt; locally and &lt;a href="https://www.thisdot.co" rel="noopener noreferrer"&gt;https://www.thisdot.co&lt;/a&gt; for the environment):&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Continuous Deployment Trigger
&lt;/h2&gt;

&lt;p&gt;Even though we could save the configuration, and create manual releases at this point, in order to continuously deploy our artifacts, we'll need to set up a trigger. Click on the lightning strike symbol that's showing up on your artifact in the pipeline section, enable the Continuous deployment trigger, and add a branch filter for the branch you want to deploy (which is master in this case). We don't need to enable the Pull request trigger for this article.&lt;/p&gt;

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

&lt;p&gt;You can save the release pipeline, and create a new release, either by running a new build, and making use of the continuous deployment trigger, or by manually creating a new release.&lt;/p&gt;

&lt;p&gt;Once the deployment is finished, navigating to one of the Azure Storage endpoint URL's should show the Angular application, including the correct environment configuration being logged to the console.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Using an Azure Storage Account to host a static web application doesn't make deploying any different from deploying to an Azure App Service. However, when we're not making use of any server-side rendering technology, using a runtime such as .NET/PHP/Java is unnecessary, so we have no need to use an App Service. &lt;/p&gt;

&lt;p&gt;Making use of Azure Storage lowers the costs since hosting itself is free, and you're only billed for the blob storage taken by the application's files. Even though App Services has free variants, once you start adding things such as custom domains, you'll need to use one of the paid variants. The cost of these are generally higher than that of an Azure Storage Account if all you're hosting is a static website.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>devops</category>
      <category>azure</category>
    </item>
    <item>
      <title>Runtime environment configuration with Angular</title>
      <dc:creator>Frederik Prijck</dc:creator>
      <pubDate>Wed, 30 Oct 2019 15:28:27 +0000</pubDate>
      <link>https://forem.com/thisdotmedia/runtime-environment-configuration-with-angular-4f5j</link>
      <guid>https://forem.com/thisdotmedia/runtime-environment-configuration-with-angular-4f5j</guid>
      <description>&lt;p&gt;When planning to deploy an Angular application, you might have the need for some configuration that can be different for every environment that's hosting the application. Even when you are only deploying to a single environment, the configuration can be different from the one used for local development. Often, a frontend application needs to communicate with a backend, whose URL can be different for each environment. In this article, we'll modify an Angular application to make use of a configurable API URL in such a way that it can differ for each environment to which you're deploying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular environments
&lt;/h2&gt;

&lt;p&gt;Angular has a built-in environment system that allows you to specify configuration for multiple environments. When building the application with a given target environment, Angular CLI will replace the environment.ts file with the content of the environment-specific environment file (e.g. environment.qa.ts, environment.prod.ts).&lt;/p&gt;

&lt;p&gt;Even though this works quite well, the downside of this is that we need to recompile and reupload the artifacts for each environment to which we are planning to deploy. As the idea behind an Azure DevOps release pipeline is to use a single build artifact, and deploy them to, theoretically, an endless amount of environments, this approach doesn't work well with the way we're setting up Continuous Deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime environment configuration
&lt;/h2&gt;

&lt;p&gt;As we need to be able to deploy our application to multiple environments using a different configuration without recompiling the application for a specific environment, we will need to have some kind of runtime configuration. This means we'll need to swap out the environment configuration after the artifacts are built.&lt;/p&gt;

&lt;p&gt;One way we can do this in an Angular application is by including a config.json file in the assets directory that contains the configuration settings. Including the JSON file in the assets directory ensures it's being copied to the dist folder when running ng build without the need to make any changes to the angular.json file. &lt;/p&gt;

&lt;p&gt;Go ahead and create the config.json file using a single apiUrl property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apiUrl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can load the config file as part of an APP_INITIALIZER, ensuring the application isn't started before the config file is loaded. Before we can include an actual APP_INITIALIZER, we will create a service that's responsible for fetching the config.json file using Angular's HttpClient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

      &lt;span class="nx"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./assets/config.json&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="nx"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once we added the above service to an Angular application, we can hook it into Angular's APP_INITIALIZER:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APP_INITIALIZER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;configFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&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;AppModule&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;Wherever we need access to the environment-specific configuration, we can inject the ConfigService, and access the config property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&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;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.css&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ng-azure-devops&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the above component, we're injecting the ConfigService in order to log the entire config object to the console. This should allow us to inspect whether or not the configuration has been set correctly for any environment. In a real application, you probably need to inject the config in the services that are responsible for calling that environment-specific API, and use the API URL to build the endpoint URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing the configuration file
&lt;/h2&gt;

&lt;p&gt;Now that we have everything in place to include configuration at runtime using a JSON file, all that's left to do is to replace that file with the environment-specific configuration, and it will be picked up when running the application.&lt;/p&gt;

&lt;p&gt;One way could be to include multiple environment configuration files in source control, (config.qa.json, config.prod.json, ...) and swap them during deployment.&lt;/p&gt;

&lt;p&gt;However, I prefer not to add a separate config file for each environment to which we're deploying. Instead, I think it's a good idea to use a single file, and update its content as part of an automated release pipeline. This allows for a separation between the code-base, and the different number of environments to which it's being deployed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://www.thisdotlabs.com"&gt;This Dot Inc.&lt;/a&gt; is a consulting company which contains two branches : the media stream and labs stream. This Dot Media is the portion responsible for keeping developers up to date with advancements in the web platform. In order to inform authors of new releases or changes made to frameworks/libraries, events are hosted, and videos, articles, &amp;amp; podcasts are published. Meanwhile, This Dot Labs provides teams with web platform expertise using methods such as mentoring and training.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>devops</category>
    </item>
    <item>
      <title>Continuously Deploying Angular to Azure App Service with Azure DevOps</title>
      <dc:creator>Frederik Prijck</dc:creator>
      <pubDate>Tue, 10 Sep 2019 19:08:22 +0000</pubDate>
      <link>https://forem.com/thisdotmedia/continuously-deploying-angular-to-azure-app-service-with-azure-devops-4hf2</link>
      <guid>https://forem.com/thisdotmedia/continuously-deploying-angular-to-azure-app-service-with-azure-devops-4hf2</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In the previous article, we saw how to integrate an Angular application with Azure DevOps so that we are continuously building our project, and publishing the output(s) as artifacts, attached to the build pipeline. &lt;/p&gt;

&lt;p&gt;In this article, we'll be creating a Release Pipeline to automatically deploy to an Azure App Service whenever such an artifact was published.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating the Release Pipeline
&lt;/h1&gt;

&lt;p&gt;We'll be creating the release pipeline in the same project as the build pipeline for which we want to deploy the artifacts.&lt;/p&gt;

&lt;p&gt;Navigate to releases from the project's sidebar, and select New pipeline.&lt;/p&gt;

&lt;p&gt;As we are going to make use of an Azure App Service to host our Angular application, select the Azure App Service deployment template, and click apply:&lt;/p&gt;

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

&lt;p&gt;Azure DevOps supports multiple stages in a single Release pipeline (e.g. Development, Testing, Production environments). For this article, we'll only be using one stage, which is already created by default, using the name Stage 1. If you want you can give it an appropriate name, or keep it as is.&lt;/p&gt;

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

&lt;p&gt;When selecting the Azure App Service deployment template, a task is automatically added to the Release pipeline. Head over to Tasks section, and fill in the required information. We'll need to select the Azure subscription. (&lt;a href="https://docs.microsoft.com/bs-latn-ba/azure/billing/billing-mca-create-subscription" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is how you can create one if you don't have it yet. Make sure you chose &lt;strong&gt;Free Trial&lt;/strong&gt; subscription to not be charged. Free Trial shouldn't be used for production) on which the App Service is running.&lt;/p&gt;

&lt;p&gt;If this is the first time you're connecting Azure DevOps to your Azure account, you will need to click Authorize. Once we've authorized Azure DevOps to communicate with our Azure Subscription, we can select the App Service that will host our application (make sure you've selected value from the dropdown, and not just typed it manually):&lt;/p&gt;

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

&lt;p&gt;If you don't have the App Service created on the &lt;a href="https://azure.microsoft.com/en-us/features/azure-portal/" rel="noopener noreferrer"&gt;Azure Portal&lt;/a&gt;, you need to open Azure Portal Dashboard, and find &lt;code&gt;App Service&lt;/code&gt; button on the left. Then, select it, and click the &lt;code&gt;Add&lt;/code&gt; button. Here what you should see:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Adding build artifacts
&lt;/h2&gt;

&lt;p&gt;Now that we've configured the destination environment for our deployment task, go to the Pipeline section (to the left of the Tasks section) to add the artifact we want to deploy. Click Add Artifact, select the Build source type (as the artifacts are stored within the build pipeline), and select the appropriate build pipeline from the source dropdown. You can keep the default values for both the version to deploy, as we always want to deploy the latest version, and the source alias. The source alias is the folder that will be used to download the artifacts. We will be using this value throughout the next steps when manipulating, and uploading the artifacts.&lt;/p&gt;

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

&lt;p&gt;This will make all artifacts that belong to the build pipeline available in the release pipeline. They will be downloaded automatically when the release pipeline is triggered.&lt;/p&gt;

&lt;p&gt;Now that we have both the artifacts, and the environment configured, we'll need to make one extra change to our deployment. For the Deploy Azure App Service task, we will need to specify which artifact it has to deploy.&lt;/p&gt;

&lt;p&gt;Our Build pipeline publishes two artifacts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code Coverage report&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ng build&lt;/code&gt; build output which we want to deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If needed, we could add an extra step to deploy the code coverage artifact, but for this article, we're only going to deploy the &lt;code&gt;ng build&lt;/code&gt; output, which is being published as an artifact with the name &lt;code&gt;web-app&lt;/code&gt;, and made available in our Azure App Service deployment step inside the &lt;code&gt;_ng-azure-devops&lt;/code&gt; directory (which is the source alias that was configured when adding the artifacts).&lt;/p&gt;

&lt;p&gt;Now go to &lt;em&gt;Tasks&lt;/em&gt; tab in our pipeline, click on deployment task, and change the &lt;em&gt;Package or Folder&lt;/em&gt; to &lt;code&gt;$(System.DefaultWorkingDirectory)/_ng-azure-devops/web-app&lt;/code&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Continuous deployment trigger
&lt;/h2&gt;

&lt;p&gt;Even though we could save the configuration, and create manual releases at this point, in order to continuously deploy our artifacts, we'll need to set up a trigger. Click on the lightning strike symbol that's showing up on your artifact in the pipeline section, enable the Continuous deployment trigger, and add a branch filter for the branch you want to deploy (which is master in this case). We don't need the Pull request trigger for this article.&lt;/p&gt;

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

&lt;p&gt;Save the changes for your release pipeline, and trigger a new build (by either making a code change or by manually queuing it from the build pipelines.&lt;/p&gt;

&lt;p&gt;Once the build and release pipelines are completed successfully, you should be able to navigate to the App Service's URL, and see the default Angular project.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Environment specific configuration
&lt;/h1&gt;

&lt;p&gt;When deploying our application, we might need some configuration that can be different for every environment that's hosting our application. As often frontend applications need to communicate with a backend, we'll be making the API URL configurable so that it can differ for each environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular environments
&lt;/h2&gt;

&lt;p&gt;Angular has a built-in environment system that allows you to specify multiple environment configurations. When building the application with a given target environment, Angular CLI will replace the environment.ts file with the content of the environment-specific environment file (e.g. environment.prod.ts)&lt;/p&gt;

&lt;p&gt;Even though this works quite well, the downside of this is that we need to recompile and reupload the artifacts for each environment to which we are planning on deploying. However, the idea behind an Azure DevOps release pipeline is to only build the artifacts once, while still being able to deploy them to, theoretically, an endless amount of environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime environment configuration
&lt;/h2&gt;

&lt;p&gt;As we need to be able to deploy our application to multiple environments using a different configuration, we will need a way to swap out environment configuration after the artifacts were built.&lt;/p&gt;

&lt;p&gt;A typical way to do this in an Angular application is by including a config.json file in the assets directory that contains the configuration. Putting it in the assets directory ensures it's being copied to the dist folder when running ng build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"apiUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can load the config file as part of an APP_INITIALIZER, ensuring the application isn't started before the config file is loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConfigService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./assets/config.json&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="nf"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APP_INITIALIZER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;configFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&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="nc"&gt;AppModule&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;Wherever we need access to the environment-specific configuration, we can inject the ConfigService to use the config property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.css&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ng-azure-devops&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above component, we're injecting the ConfigService in order to log the entire config object to the console. This should allow us to inspect whether or not the configuration has been set correctly for our environment. In a real application, you probably need to inject the config in your services that are responsible for calling that environment-specific API.&lt;/p&gt;

&lt;p&gt;We don't need to add a separate config file for each environment to which we're deploying. Instead, we'll use a single file, and update its content as part of the release pipeline. This allows for a separation between your code-base, and the different amount of environments it's being deployed to. &lt;/p&gt;

&lt;p&gt;Adjust tests, and make sure they pass. Then commit, and push the changes to the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying config.json on Azure DevOps
&lt;/h2&gt;

&lt;p&gt;We'll need to modify the contents of config.json as part of a task in the release pipeline, move over to the release pipeline, edit it, and add a File transform task before deploying our application.&lt;/p&gt;

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

&lt;p&gt;There are three important parts of this task that we need to configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Package or folder: points to the correct artifact for which we want to transform a file&lt;/li&gt;
&lt;li&gt;File format: Azure DevOps' File Transform task supports both XML and JSON. We'll be using JSON for this article.&lt;/li&gt;
&lt;li&gt;Target files: Include the path to the config.json file, relative to the root of your artifact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all we need to do in order for the File Transform task to start processing the config.json file prior to deploying it.&lt;/p&gt;

&lt;p&gt;In order to define what names and values it should use while transforming the config file, we will need to create variables as part of the release pipeline. The file transform task will try and find all variable's names, and update its value accordingly.&lt;/p&gt;

&lt;p&gt;For this article, all we need is an apiUrl variable with a value different from what we have locally (I went with &lt;a href="http://localhost" rel="noopener noreferrer"&gt;localhost&lt;/a&gt; locally and thisdot.co for the environment):&lt;/p&gt;

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

&lt;p&gt;If you have a more complex configuration object, using nested paths, you need to specify them using a JSONPath expression, see &lt;a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/file-transform?view=azure-devops" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/file-transform?view=azure-devops&lt;/a&gt; for more information on the file transform task.&lt;/p&gt;

&lt;h1&gt;
  
  
  Enabling JSON files on Azure
&lt;/h1&gt;

&lt;p&gt;An Azure App Service doesn't allow JSON files to be served by default. In order to do so, we need to add a web.config file (more info on configuring system.webServer can be found at &lt;a href="https://docs.microsoft.com/en-us/iis/configuration/system.webserver/" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/iis/configuration/system.webserver/&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;system.webServer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;staticContent&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;remove&lt;/span&gt; &lt;span class="na"&gt;fileExtension=&lt;/span&gt;&lt;span class="s"&gt;".json"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;mimeMap&lt;/span&gt; &lt;span class="na"&gt;fileExtension=&lt;/span&gt;&lt;span class="s"&gt;".json"&lt;/span&gt; &lt;span class="na"&gt;mimeType=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/staticContent&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/system.webServer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could add this config file to our git repository, and ensure it's also copied to the dist folder when running &lt;code&gt;ng build.&lt;/code&gt; This get's a little complex if all we need is to enable JSON.&lt;/p&gt;

&lt;p&gt;Luckily, we can also add a file as part of a release pipeline. As our web.config file will be very small, I'll be using this approach. This, again, keeps our source-code independent on where we'll be hosting it.&lt;/p&gt;

&lt;p&gt;Move over to the release pipeline, and add a new task, either before or after transforming the config file. We do need to ensure this task is added before deploying the artifacts, so ensure it's executed before the deployment task.&lt;/p&gt;

&lt;p&gt;The task for creating a file is not part of Azure DevOps itself. You can get it for free through the Marketplace: &lt;a href="https://marketplace.visualstudio.com/items?itemName=eliostruyf.build-task" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=eliostruyf.build-task&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once installed, edit the release pipeline, and add a new File Creator task, providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file path: this is the path, including filename, for the file that's being created&lt;/li&gt;
&lt;li&gt;file content: Any content that should go in the created file.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Save the release pipeline, and create a new release (either by triggering a new build, or manually creating a release using the last known artifacts).&lt;/p&gt;

&lt;p&gt;Once the release was completed successfully, navigating to the URL of the App Service should show the default Angular application as well as a console output showing the environment-specific configuration.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Azure DevOps makes setting up continuous deployment a breeze. In only a few steps, we have integrated an Angular application to automatically deploy our artifacts to one or more environments, providing each environment with the required environment-specific configuration.&lt;/p&gt;

&lt;p&gt;This should take away a lot of time from manual deployments, allowing your team to focus on the quality of the software instead.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was written by Frederik Prijck who is a Software Engineer at &lt;a href="https://thisdot.co" rel="noopener noreferrer"&gt;This Dot&lt;/a&gt;.&lt;/em&gt;   &lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can follow him on Twitter at &lt;a class="mentioned-user" href="https://dev.to/frederikprijck"&gt;@frederikprijck&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Need JavaScript consulting, mentoring, or training help? Check out our list of services at &lt;a href="https://thisdot.co" rel="noopener noreferrer"&gt;This Dot Labs&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>azure</category>
      <category>devops</category>
    </item>
    <item>
      <title>Continuously Integrating Angular with Azure DevOps</title>
      <dc:creator>Frederik Prijck</dc:creator>
      <pubDate>Thu, 05 Sep 2019 23:23:48 +0000</pubDate>
      <link>https://forem.com/thisdotmedia/continuously-integrating-angular-with-azure-devops-2k9l</link>
      <guid>https://forem.com/thisdotmedia/continuously-integrating-angular-with-azure-devops-2k9l</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Azure DevOps is a Microsoft product that, alongside other features such as Source Code Management and Project Management, allows your team to set up Continuous Integration for their project(s).&lt;/p&gt;

&lt;p&gt;Continuous Integration is a development practice that enables your team to improve quality, and deliver more stable software, benefiting both the team, and the end-user.&lt;/p&gt;

&lt;p&gt;With continuous integration, several checks (referred to as a pipeline) are automatically run on certain triggers, such as a code change. These checks typically ensure that the code can be compiled successfully, and that all tests are passing. Automatically running these checks, in an isolated environment, takes away most of the issues that do work on my machine, and ensures our code-base is in good health at all times.&lt;/p&gt;

&lt;h1&gt;
  
  
  Angular application
&lt;/h1&gt;

&lt;p&gt;For this demo, we'll be using a default Angular CLI application for which we'll be adding a Build pipeline on Azure DevOps.&lt;/p&gt;

&lt;p&gt;Ensure you have the latest version of &lt;code&gt;@angular/cli&lt;/code&gt; by running &lt;code&gt;npm install --global @angular/cli&lt;/code&gt;, and create a new angular application using &lt;code&gt;ng new ng-azure-devops --skipInstall=true&lt;/code&gt;; we don't need Angular routing, and you can pick the stylesheet format of your choice.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating the Azure DevOps project
&lt;/h1&gt;

&lt;p&gt;If you don't have a project on Azure DevOps yet, head over to &lt;a href="https://dev.azure.com" rel="noopener noreferrer"&gt;https://dev.azure.com&lt;/a&gt;, and create a new one for this demo, provide a name, and (optionally) a description. You can choose which visibility you want. I'm going with public, but private would work just fine.&lt;/p&gt;

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

&lt;p&gt;Once the project is created, you'll be taken to the project's dashboard.&lt;/p&gt;

&lt;h1&gt;
  
  
  Adding the code to Azure DevOps
&lt;/h1&gt;

&lt;p&gt;On your project's sidebar, you should find Repos. Clicking it will take you to the default Git repository that was created for this project. You can add more repositories in your project if needed, so you don't need to create multiple projects if your application is spread over multiple Git repositories. For this article, we only need a single repository, so we're going to use the one that was created by default.&lt;/p&gt;

&lt;p&gt;The repository's home should tell you it's empty, and provide you with instructions to push your code to the repository. &lt;/p&gt;

&lt;p&gt;You'll need to &lt;a href="https://docs.microsoft.com/en-us/azure/devops/repos/git/use-ssh-keys-to-authenticate?view=azure-devops" rel="noopener noreferrer"&gt;configure your SSH key&lt;/a&gt; or use the &lt;a href="https://docs.microsoft.com/en-us/azure/devops/repos/git/set-up-credential-managers?view=azure-devops" rel="noopener noreferrer"&gt;Git credential manager&lt;/a&gt; in order to be able to commit to a repository on Azure DevOps.&lt;/p&gt;

&lt;p&gt;Once authentication is configured, commit, and push the Angular project that we've created using the Angular CLI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to use GitHub, you can add your code to Github, and connect your Github account in Azure DevOps by going to the Project Settings &amp;gt; Github Connections (it's categorized under Boards in the Project Settings). You'll need to select the GitHub organization, as well as the repositories you want to integrate into the current project. &lt;/p&gt;

&lt;p&gt;The next steps will work identical for both GitHub repositories and Azure DevOps repositories.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Creating a build pipeline
&lt;/h1&gt;

&lt;p&gt;Now that we have our code either in an Azure DevOps repository or on GitHub (with our GitHub account connected through Project Settings), we can create a build pipeline for that repository by navigating to Pipelines from the project's sidebar.&lt;/p&gt;

&lt;p&gt;Click on New Pipeline, select Azure Repos Git or GitHub (or anything else if you're using any of the other available options), click the corresponding repository (if you selected GitHub, you'll be prompted to authorize Azure Pipelines) and select Node.js with Angular as the pipeline configuration. This will preconfigure the pipeline to run &lt;code&gt;npm install&lt;/code&gt;, and &lt;code&gt;ng build —-prod&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the review step, we'll see an example of the yaml file that's being generated by Azure DevOps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Node.js with Angular&lt;/span&gt;
&lt;span class="c1"&gt;# Build a Node.js project that uses Angular.&lt;/span&gt;
&lt;span class="c1"&gt;# Add steps that analyze code, save build artifacts, deploy, and more:&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript&lt;/span&gt;

&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodeTool@0&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;versionSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;10.x'&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Node.js'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;npm install -g @angular/cli&lt;/span&gt;
    &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="s"&gt;ng build --prod&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default configuration will trigger for each change on the master branch. It'll use an ubuntu image, and executes two steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Node.js&lt;/li&gt;
&lt;li&gt;Install dependencies and build the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clicking Save And Run will add the yaml file your repository, keeping your build pipeline configuration together with the source code. We can even make changes to the yaml file locally, and commit them, Azure DevOps will pick up these changes, and update your build pipeline accordingly.&lt;/p&gt;

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

&lt;p&gt;Creating a separate branch, and starting a pull request allows you to not merge this configuration in master before it's completely configured in the way that you want. To simplify things, we'll be committing changes directly to master.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you prefer to use a separate branch, you can still do so. However, do know that builds are not triggered automatically as long as there's no azure-pipelines.yml file on master branch. This means you'll need to manually trigger builds until your configuration is merged into master.&lt;br&gt;
In case of creating a pipeline, clicking Save and Run will run it either way, but as we'll be making changes to the source code, builds will not be triggered automatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the configuration file has been saved, a new build is triggered, and after a while, all steps should have been completed successfully.&lt;/p&gt;

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

&lt;p&gt;Some of the above steps are added by Azure DevOps (Prepare, Initialize, Checkout, Post Checkout and Finalize), while others are defined as part of our yaml configuration (Install Node.js and npm install and build). If you want to get more information for a specific step, clicking it will show all the output for that particular step:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Finetuning the build pipeline
&lt;/h2&gt;

&lt;p&gt;We haven't made any changes to the default Node.js with Angular configuration yet. Before adding additional steps, let's change the configuration in such a way that installing the dependencies, and building the project are two separate steps: remove the global installation of &lt;code&gt;@angular/cli&lt;/code&gt; and make use of &lt;a href="https://github.com/npm/npx" rel="noopener noreferrer"&gt;npx&lt;/a&gt; instead.&lt;br&gt;
To edit the build pipeline, click pipelines from the project's sidebar, select the corresponding build pipeline, and click edit on the upper right corner:&lt;/p&gt;

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

&lt;p&gt;When clicking edit, Azure DevOps will open an editor for the yaml file that was created earlier. Edit the contents of the file so that it's identical to the yaml configuration below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Node.js with Angular&lt;/span&gt;
&lt;span class="c1"&gt;# Build a Node.js project that uses Angular.&lt;/span&gt;
&lt;span class="c1"&gt;# Add steps that analyze code, save build artifacts, deploy, and more:&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript&lt;/span&gt;

&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodeTool@0&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;versionSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;10.x'&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Setup&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Environment'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Dependencies'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx ng build --prod&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saving the configuration should trigger a new build. You should see the Install Dependencies, and Build steps being two individual steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Felu2ad8t6qleu8a0r8jr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Felu2ad8t6qleu8a0r8jr.png" alt="Separated npm install and ng build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Azure DevOps is now integrated to run &lt;code&gt;ng build&lt;/code&gt; on every commit to master. Apart from building the project, we might also want to run all of the unit tests so that we know that all of the code is still working as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Unit Tests on Azure DevOps
&lt;/h3&gt;

&lt;p&gt;Before we'll be able to run the unit tests on Azure DevOps, we'll need to make some changes to the project that was generated using Angular CLI.&lt;/p&gt;

&lt;p&gt;As the host, on which the pipeline is executing all of the automated steps, has no Chrome installed, we'll need to add puppeteer to be able to use ChromeHeadless on Azure DevOps.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install puppeteer --save-dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Open karma.conf.js, and add the following to the top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CHROME_BIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will load puppeteer, and use its executablePath for the CHROME_BIN environment variable, allowing for karma to use puppeteer when running Chrome or ChromeHeadless.&lt;/p&gt;

&lt;p&gt;As a last step, update the karma.conf.js file to use the &lt;strong&gt;ChromeHeadless&lt;/strong&gt; browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="nl"&gt;browsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ChromeHeadless&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Updating karma.conf.js to use puppeteer will use puppeteer on your local machine as well. If this is not something you want, you can create a copy of the karma.conf.js file, name it karma.conf.ci.js (or anything you like), and pass it to the &lt;code&gt;ng test --karmaConfig karma.conf.ci.js&lt;/code&gt; command when running it in the pipeline (we'll be configuring this later).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Commit and push these changes to the repository. This will trigger a build, but this won't execute the tests yet as we first have to update the build pipeline to run the tests. Add a new step to the configuration in Azure DevOps, either before or after the Build step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx ng test --watch=false&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Tests'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once saved, the build should be triggered and completed successfully, including a Tests step. Clicking on the Tests step should show you the same information you would get when running &lt;code&gt;ng test&lt;/code&gt; locally.&lt;/p&gt;

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

&lt;p&gt;Even though this gives us all the information we need regarding the tests, Azure DevOps has a built-in feature to show tests reports, enabling you to more easily navigate successful/failed tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Publishing test results to Azure DevOps
&lt;/h3&gt;

&lt;p&gt;We'll need to publish the test results in a format that can be interpreted by Azure DevOps. In order to understand what formats are supported, lets head over to our build pipeline, and click edit.&lt;/p&gt;

&lt;p&gt;On the right side of the build pipeline's edit screen, you should see a list of available tasks. Search for the task named &lt;strong&gt;Publish Test Results&lt;/strong&gt;, and select it.&lt;br&gt;
In the task details screen, you'll see a drop-down for the test result format. You can use any of the available formats, as long as your test runner allows you to export the results in the given format. As Karma supports JUnit, we'll be using that one.&lt;/p&gt;

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

&lt;p&gt;Once added, the yaml configuration will now contain a new section for the PublishTestResults task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishTestResults@2&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;testResultsFormat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JUnit'&lt;/span&gt;
    &lt;span class="na"&gt;testResultsFiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/TEST-*.xml'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure this section was added after the step that's running &lt;code&gt;npx ng test&lt;/code&gt;, and make the following changes so that it has a displayName, and provide it with a condition so that it publishes test results even when the tests have failed (because we also want to be able to inspect failing tests).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishTestResults@2&lt;/span&gt;
  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;succeededOrFailed()&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;testResultsFormat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JUnit'&lt;/span&gt;
    &lt;span class="c1"&gt;# Make sure you've changed testResultsFiles to the one below &lt;/span&gt;
    &lt;span class="na"&gt;testResultsFiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/TESTS-*.xml'&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Test&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Results'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we haven't configured our Angular application to publish test results in the JUnit format yet, the build that's being triggered upon saving the above changes will not publish any test results yet.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Configuring Karma to publish JUnit
&lt;/h3&gt;

&lt;p&gt;Install the appropriate reporter for JUnit in the Angular project by running &lt;code&gt;npm install karma-junit-reporter --save-dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Update karma.conf.js by including the &lt;code&gt;karma-junit-reporter&lt;/code&gt; in the plugins and adding &lt;code&gt;junit&lt;/code&gt; as one of the reporters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;...,&lt;/span&gt;
      &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;karma-junit-reporter&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;span class="na"&gt;reporters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kjhtml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;junit&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;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all we have to change in order to prepare our unit tests to be fully integrated into Azure DevOps. Commit and push the changes you made to the repository.&lt;/p&gt;

&lt;p&gt;This should trigger a new build, once that's finished you should see the 3 default tests showing up in the Tests section on the build details (you might need to check the filters, by default it only shows failed tests). &lt;/p&gt;

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

&lt;p&gt;By integrating our tests with Azure DevOps, we have a structured UI for tracking all of our unit tests. Whenever a test fails, we don't have to scroll through the console output on the tests step. Instead, we can inspect the tests altogether and filter to only show those we want.&lt;/p&gt;

&lt;p&gt;If we'd have a failing test, we can even click on it to show the details of what went wrong, showing the same information we would get in the console:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Showing Code Coverage in a Build Pipeline
&lt;/h3&gt;

&lt;p&gt;Azure DevOps provides us with the ability to show an overview of the code coverage of our application's unit tests. To do so, just like publishing the test results in a specific format, we'll need to publish the code coverage results in a supported format. Currently, Azure DevOps supports both Cobertura and JaCoCo. Istanbul, the code coverage tool used by the Angular CLI, has support for Cobertura built-in, so we don't need to add any dependency in order to use Cobertura.&lt;/p&gt;

&lt;p&gt;We'll need to update the karma configuration to include Cobertura in the reports for the coverageIstanbulReporter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;coverageIstanbulReporter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./coverage/ng-azure-devops&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lcovonly&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cobertura&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;fixWebpackSourcePaths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate a file named cobertura-coverage.xml in the ./coverage/ng-azure-devops directory. We will be needing this path in the Build pipeline.&lt;/p&gt;

&lt;p&gt;Commit, and push these changes to your repository (this will trigger a build that doesn't publish the code coverage yet), and head over to the build pipeline's configuration on Azure DevOps and add the following step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishCodeCoverageResults@1&lt;/span&gt;
  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;succeededOrFailed()&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;codeCoverageTool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Cobertura'&lt;/span&gt;
    &lt;span class="na"&gt;summaryFileLocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Build.SourcesDirectory)/coverage/ng-azure-devops/cobertura-coverage.xml'&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Code&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Coverage&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Results'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like with publishing the test results, we want to include the code coverage results for failed tests as well. That's why we're setting the condition to &lt;code&gt;succeededOrFailed()&lt;/code&gt;. We also need to provide the location of the summary file (created by istanbul), and ensure this aligns with the location of the file, generated by istanbul.&lt;/p&gt;

&lt;p&gt;In order to be able to publish the code coverage results, we'll need to run &lt;code&gt;ng test&lt;/code&gt; using the &lt;code&gt;--code-coverage&lt;/code&gt; flag. Update the corresponding step in the build pipeline configuration to include that flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx ng test --watch=false --codeCoverage=true&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Tests'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saving the configuration should run a build that, once completed, shows a Code Coverage section on the build's details page.&lt;/p&gt;

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

&lt;p&gt;Publishing the code coverage results will also create a report and make it available as an artifact, attached to your build. If you ever wish to inspect the report, you can download it by opening the Artifacts menu on the upper right corner. Generally, you should find most information on the Code Coverage tab in Azure DevOps itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Linting
&lt;/h3&gt;

&lt;p&gt;We can ensure the build pipeline fails when code does not follow the coding guidelines by integrating our linter. Let's add a step to run &lt;code&gt;npx ng lint&lt;/code&gt;, either before or after the Build step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx ng lint&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Code&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Analysis'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add a basic integration with our linter. Azure DevOps doesn't have a built-in way to keep track of code quality. However, it does have tasks available to integrate 3th party tools such as SonarQube if you would want to integrate with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing Artifacts
&lt;/h2&gt;

&lt;p&gt;Now that we have an automated way to verify that the source code compiles, all of the unit tests are running successfully, and code matches with our coding guidelines, we can publish the output of &lt;code&gt;ng build&lt;/code&gt; so that we can use it to deploy, either manual or automated.&lt;/p&gt;

&lt;p&gt;Add the following step to the end of the Build Pipeline configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishBuildArtifacts@1&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PathtoPublish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dist/ng-azure-devops'&lt;/span&gt;
    &lt;span class="na"&gt;ArtifactName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;web-app'&lt;/span&gt;
    &lt;span class="na"&gt;publishLocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Container'&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Artifacts'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to publish the artifacts, we need to provide the PathtoPublish, which is the output folder of the &lt;code&gt;ng build&lt;/code&gt; command, an artifact name, and the publishLocation. Providing "Container" as the publishLocation means that it will publish the artifacts as part of the Azure Pipeline itself. &lt;/p&gt;

&lt;p&gt;Save the changes, and wait for the build to complete. Once completed, you can download the artifacts from the build result's page:&lt;/p&gt;

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

&lt;p&gt;Every time a build is successful, the latest version will be published as an artifact. This allows us to either download it, or use it in an automated way to deploy our application.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;With Azure DevOps, we can easily integrate our Angular application in such a way that we can ensure our codebase is in good health at all times. This should reduce the risk of releasing bugs, resulting in more stable software, and both a happier team, and end-user.&lt;/p&gt;

&lt;p&gt;We're also creating our build artifacts in an automated way, reducing time, and complexity, to deploy our application. &lt;/p&gt;

&lt;p&gt;In a future article, we'll set up a release pipeline to automatically deploy the build artifacts.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was written by Frederik Prijck who is a Software Engineer at &lt;a href="https://thisdot.co" rel="noopener noreferrer"&gt;This Dot&lt;/a&gt;.&lt;/em&gt;   &lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can follow him on Twitter at &lt;a class="mentioned-user" href="https://dev.to/frederikprijck"&gt;@frederikprijck&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Need JavaScript consulting, mentoring, or training help? Check out our list of services at &lt;a href="https://thisdot.co" rel="noopener noreferrer"&gt;This Dot Labs&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>azure</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
