<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ennio Bozzetti</title>
    <description>The latest articles on DEV Community by Ennio Bozzetti (@enniob).</description>
    <link>https://dev.to/enniob</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%2F145827%2F8f392394-a03e-4b18-8915-666401ef0cbc.jpeg</url>
      <title>DEV Community: Ennio Bozzetti</title>
      <link>https://dev.to/enniob</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/enniob"/>
    <language>en</language>
    <item>
      <title>CyberTabletop CLI — I turned GitHub Copilot into a tabletop exercise facilitator</title>
      <dc:creator>Ennio Bozzetti</dc:creator>
      <pubDate>Fri, 13 Feb 2026 01:21:00 +0000</pubDate>
      <link>https://dev.to/enniob/cybertabletop-cli-i-turned-github-copilot-into-a-tabletop-exercise-facilitator-e9o</link>
      <guid>https://dev.to/enniob/cybertabletop-cli-i-turned-github-copilot-into-a-tabletop-exercise-facilitator-e9o</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-01-21"&gt;GitHub Copilot CLI Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Most security teams never practice incident response. Tabletop exercises are the fix — you sit around a table, someone reads a scenario, and you talk through what you'd do. But running one takes prep, a facilitator, and scheduling time that nobody has.&lt;/p&gt;

&lt;p&gt;So I built a CLI game that does it for you. You pick a ransomware scenario, make decisions each turn, and the situation evolves based on what you chose. The twist: GitHub Copilot CLI is the facilitator. It generates the scenarios, writes the narrative, decides the consequences, and keeps the pressure on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CyberTabletop CLI&lt;/strong&gt; is a turn-based cybersecurity tabletop exercise game that runs entirely in your terminal. You play as the incident response lead at a pharmaceutical company that just got hit by ransomware.&lt;/p&gt;

&lt;p&gt;Every turn, you get a situation update and 3-4 choices. Pick one, and the game advances the clock, adjusts risk scores, changes asset statuses, and generates the next set of problems. The whole thing is driven by &lt;code&gt;copilot -p&lt;/code&gt; calls under the hood — there's no hardcoded scenario tree, so every playthrough is different.&lt;/p&gt;

&lt;p&gt;Here's what a session looks like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You run &lt;code&gt;python main.py start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copilot generates three ransomware variants with different entry vectors (phishing, supply chain compromise, insider threat, etc.)&lt;/li&gt;
&lt;li&gt;You pick one&lt;/li&gt;
&lt;li&gt;The game bootstraps a full company profile — assets, security tools, risk levels, the works&lt;/li&gt;
&lt;li&gt;Then you're in the loop. Each turn: read the update, pick A/B/C/D, deal with the consequences&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The game tracks three risk dimensions (operational, data, legal) on a 0-100 scale, manages an asset list with real-time status changes, and keeps an in-game clock that advances 5-20 minutes per turn depending on what's happening.&lt;/p&gt;

&lt;p&gt;When you're done, you can generate an AI debrief with &lt;code&gt;/debrief&lt;/code&gt; or export a full markdown transcript with &lt;code&gt;/export&lt;/code&gt; — useful for sharing with your team or writing up what happened.&lt;/p&gt;

&lt;p&gt;The interactive session supports slash commands so you never leave the game loop: &lt;code&gt;/status&lt;/code&gt; to check your risk scores, 'json' to peek at raw state, &lt;code&gt;/help&lt;/code&gt; to see what's available, and &lt;code&gt;/exit&lt;/code&gt; to save and quit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech stack:&lt;/strong&gt; Python, Typer, Rich (for terminal formatting), and GitHub Copilot CLI as the AI backend. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/48_8OKKCyRM"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot CLI
&lt;/h2&gt;

&lt;p&gt;This project uses Copilot CLI in a way I haven't seen before — not just as a development tool, but as the &lt;strong&gt;runtime AI engine&lt;/strong&gt; for the game itself.&lt;/p&gt;

&lt;p&gt;Every time the game needs content, it shells out to &lt;code&gt;copilot -p "&amp;lt;prompt&amp;gt;"&lt;/code&gt; and parses the JSON that comes back. That means Copilot is generating scenarios, writing incident narratives, calculating consequences, and producing debriefs. It's doing the work of a human facilitator, live, in your terminal.&lt;/p&gt;

&lt;p&gt;Getting this to work reliably was the interesting part.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt engineering for structured output
&lt;/h3&gt;

&lt;p&gt;The biggest challenge was getting consistent JSON back from a CLI tool that's designed for conversational output. I ended up embedding full JSON schemas directly in every prompt, plus a system prompt (&lt;code&gt;FACILITATOR_RULES&lt;/code&gt;) that hammers home the constraints: JSON only, no markdown, no backticks, no commentary, ransomware only, 3-4 choices per turn.&lt;/p&gt;

&lt;p&gt;Each prompt type (start menu, bootstrap, turn, debrief) has its own schema so Copilot knows exactly what shape to return. The turn prompt also sends the full game state — risk levels, asset statuses, signals, flags, recent player actions — so Copilot can write consequences that actually make sense in context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defensive JSON parsing
&lt;/h3&gt;

&lt;p&gt;Even with strict prompts, Copilot sometimes wraps output in markdown fences or adds commentary. So I built a defensive parser that first tries &lt;code&gt;json.loads()&lt;/code&gt; on the raw output, then falls back to regex-extracting the first &lt;code&gt;{...}&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;On Windows, I hit another curveball — Copilot occasionally returned PowerShell object notation (&lt;code&gt;@{}&lt;/code&gt;, &lt;code&gt;@()&lt;/code&gt;) instead of JSON. So there's a converter that handles that too. It's not pretty, but it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  State machine design
&lt;/h3&gt;

&lt;p&gt;The game state is a single dict that gets passed to Copilot each turn. Copilot returns a "state patch" — risk deltas, asset updates, new signals, flag changes — and the engine applies it. This keeps the game consistent across turns without Copilot needing to remember anything.&lt;/p&gt;

&lt;p&gt;I kept the state compact on purpose. Only the last 6 player actions get sent back, and the state only includes what's needed for the next decision. More context = more tokens = slower and less stable responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  What worked well
&lt;/h3&gt;

&lt;p&gt;Copilot CLI turned out to be surprisingly good at this. The scenarios feel realistic, the consequences track logically, and it naturally escalates tension over time. Constraining it to pharma + ransomware helped a lot — narrow scope = more consistent output.&lt;/p&gt;

&lt;p&gt;The 90-second subprocess timeout was generous enough that I never hit it in practice, even with the longer bootstrap prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd do differently
&lt;/h3&gt;

&lt;p&gt;The prompt engineering is brittle. If Copilot changes how it handles system instructions, the JSON parsing could break. A future version would probably benefit from retry logic and schema validation on the response.&lt;/p&gt;

&lt;p&gt;I'd also like to support more scenario types beyond ransomware and more industries beyond pharma, but for an MVP the narrow focus was the right call. It kept the prompts stable and the output quality high.&lt;/p&gt;




&lt;p&gt;This was a fun build. There's something satisfying about turning a conversational AI tool into a structured game engine — and the result is something I'd actually use to run exercises with my team.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>cli</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Digital Storyteller: A Multimodal Applet</title>
      <dc:creator>Ennio Bozzetti</dc:creator>
      <pubDate>Thu, 04 Sep 2025 20:12:43 +0000</pubDate>
      <link>https://dev.to/enniob/digital-storyteller-a-multimodal-applet-2930</link>
      <guid>https://dev.to/enniob/digital-storyteller-a-multimodal-applet-2930</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-ai-studio-2025-09-03"&gt;Google AI Studio Multimodal Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;The Digital Storyteller is an interactive applet that leverages Google's Gemini models to create imaginative stories from images. Users can upload an image and, optionally, provide a text prompt to guide the narrative. The app generates a short, creative story and then converts that story into an audio file that can be played back. This provides a complete multimedia experience, transforming a static image into a dynamic, narrated tale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ai.studio/apps/drive/1nneUvotoe6nJH__OjF3AZEa_rh2SUZCI" rel="noopener noreferrer"&gt;Demo link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/pfsUfHwLFls"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Google AI Studio
&lt;/h2&gt;

&lt;p&gt;I used Google AI Studio as the development environment for building this applet. I accessed and utilized the Gemini models directly through their APIs to power the app's core functionalities. The app relies on gemini-2.5-flash to process the image and text prompts and generate the story. It then uses the gemini-2.5-flash-preview-tts model to create the audio from the text. The app was built locally and is ready to be deployed to a platform like Cloud Run, as required by the challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multimodal Features
&lt;/h2&gt;

&lt;p&gt;This applet demonstrates multimodal functionality in two key ways:&lt;/p&gt;

&lt;p&gt;Multimodal Content Understanding: The app takes two different modalities as input: an image (visual) and a text prompt. It uses the powerful gemini-2.5-flash model to understand the content of both inputs and then combine them to create a single, cohesive text output (the story).&lt;/p&gt;

&lt;p&gt;Multimodal Content Generation: After the story is created, the app uses the gemini-2.5-flash-preview-tts model to convert the text of the story into audio data. This showcases the ability to generate new content in a different modality from a text input, providing a richer, more engaging user experience.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>How to group an object by property</title>
      <dc:creator>Ennio Bozzetti</dc:creator>
      <pubDate>Mon, 30 Dec 2019 17:18:35 +0000</pubDate>
      <link>https://dev.to/enniob/how-to-group-an-object-by-property-fbf</link>
      <guid>https://dev.to/enniob/how-to-group-an-object-by-property-fbf</guid>
      <description>&lt;p&gt;Here is a quick tip on how you can use &lt;strong&gt;Javascript reduce&lt;/strong&gt; to group an array of objects by a property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const students = [
  { name: 'John', grade: 1 },
  { name: 'James', grade: 1 },
  { name: 'Ryan', grade: 2 },
  { name: 'Matt', grade: 2 },
  { name: 'Joe', grade: 1 }
];

const groupedStudents = students.reduce((prev, cur) =&amp;gt; {
  if (!prev[cur['grade']]) {
    prev[cur['grade']] = [];
  }

  prev[cur['grade']].push(cur);

  return prev;
}, {});

console.log(groupedStudents);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your developer console and expand the object you see. As you can see, the object has two property 1 and 2. They are the grades from the original array, and since we ask to group the new object by grade, that is why our new object has property 1 and property 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54zf7yeg6s35onlk6j7s.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54zf7yeg6s35onlk6j7s.PNG" alt="Group Object" width="211" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if you expand the property 1, you can see that we have an array of students and inside that array, you find all the students from grade 1, and if you do the same for property 2 you can see the students from grade 2&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vxoj0a0m121cb7vgv5n.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vxoj0a0m121cb7vgv5n.PNG" alt="Detailed View" width="217" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, this is a beneficial function, and we could reuse it in many places on our application. To be able to reuse it, we need to convert to a function, and that way we can pass the property we want to group by instead of hardcoding.&lt;/p&gt;

&lt;p&gt;Here is our new function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function groupedObj(objArray, property) {
  return objArray.reduce((prev, cur) =&amp;gt; {
    if (!prev[cur[property]]) {
      prev[cur[property]] = [];
    }
    prev[cur[property]].push(cur);

    return prev;
  }, {});
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to connect your Angular application to Firebase</title>
      <dc:creator>Ennio Bozzetti</dc:creator>
      <pubDate>Tue, 01 Oct 2019 18:57:13 +0000</pubDate>
      <link>https://dev.to/enniob/how-to-connect-your-angular-application-to-firebase-4e2n</link>
      <guid>https://dev.to/enniob/how-to-connect-your-angular-application-to-firebase-4e2n</guid>
      <description>&lt;p&gt;In this post, I will show you how you can connect your reactive form with Google Firebase. If you missed part 1 on how to make a reactive form, here is a link to it.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/enniob" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F145827%2F8f392394-a03e-4b18-8915-666401ef0cbc.jpeg" alt="enniob"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/enniob/how-to-create-a-reactive-form-in-angular-2c1d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to create a reactive form in Angular&lt;/h2&gt;
      &lt;h3&gt;Ennio Bozzetti ・ Jul 10 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#angular&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Create your Firebase account
&lt;/h2&gt;

&lt;p&gt;If you don’t have a Firebase account go to &lt;a href="https://firebase.google.com/" rel="noopener noreferrer"&gt;https://firebase.google.com/&lt;/a&gt; and follow the steps on how to create your Firebase account. If you already have an account, log in to your account, and create a new project.&lt;/p&gt;

&lt;p&gt;This should take a few seconds, and once done it should redirect you to a new page.&lt;/p&gt;

&lt;p&gt;For now, that is all we need to do on Firebase, we will get back to it once we set up our application.&lt;/p&gt;

&lt;p&gt;I create a starter repo that contains the basic setup for the project. So, let’s clone the repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/enniob/reactiveForms.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the repo is cloned, change to the following branch &lt;strong&gt;&lt;em&gt;remotes/origin/Reactive-Form-Part-2&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout Reactive-Form-Part-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s take a look at the current state of the application. Open the console, and type &lt;code&gt;npm install&lt;/code&gt; this will install all the dependencies needed. Once that is done, run &lt;code&gt;ng serve -o&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can see that our application has 2 pages; Home and login. If you click on login you will see a login form where you can enter a username and password. If you enter the login and password and click login, all we are doing is &lt;code&gt;console.log&lt;/code&gt; the formData.&lt;/p&gt;

&lt;p&gt;So now that we know our application is working, let’s connect our login form with Firebase.&lt;/p&gt;

&lt;p&gt;We will use the AngularFire library to make the connections between your Angular project and Firebase.&lt;/p&gt;

&lt;p&gt;First, let’s install AngularFire &lt;code&gt;npm install firebase @angular/fire --save&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once the installation is done, we need to change &lt;code&gt;enviroment.ts&lt;/code&gt; file to include the Firebase configuration for our application.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;enviroment.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const environment = {
  production: false,
  firebase: {
    apiKey: 'xxxxxxxxxxxxxxx',
    authDomain: 'xxxxxxxxxxxxxxx',
    databaseURL: 'xxxxxxxxxxxxxxx',
    projectId: 'xxxxxxxxxxxxxxx',
    storageBucket: 'xxxxxxxxxxxxxxx',
    messagingSenderId: 'xxxxxxxxxxxxxxx',
    appId: 'xxxxxxxxxxxxxxx'
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get that information from the firebase website, open your project and click on add app.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;app.module.ts&lt;/code&gt; and import &lt;strong&gt;AngularFireModule, and AngularFireAuthModule&lt;/strong&gt;. We also need to import our &lt;code&gt;environment.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { environment } from '../environments/environment';

// MATERIAL
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';
import { HomeComponent } from './home/home.component';

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    HomeComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    BrowserAnimationsModule,
    MatCardModule,
    MatFormFieldModule,
    MatInputModule,
    MatButtonModule,
    MatToolbarModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireAuthModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open &lt;code&gt;login.component.ts&lt;/code&gt; and let's add the following code, so we can connect our form with &lt;/p&gt;

&lt;p&gt;&lt;code&gt;login.component.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

import { FormGroup, FormControl, Validators } from '@angular/forms';
import { AngularFireAuth } from '@angular/fire/auth';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {

  loginForm = new FormGroup({
    userName: new FormControl('', Validators.compose([Validators.required])),
    password: new FormControl('', Validators.compose([Validators.required]))
  });

  constructor(private afAuth: AngularFireAuth, private router: Router) { }

  ngOnInit() {
  }

  doLogin(formData: FormGroup) {
    if (formData.valid) {
      this.afAuth.auth.signInWithEmailAndPassword(formData.value.userName, formData.value.password)
        .then(loginResponse =&amp;gt; {
          console.log(loginResponse);
          this.router.navigate(['/']);
        })
        .catch(error =&amp;gt; {
          console.log(error);
        });
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see on the code above, we imported &lt;code&gt;import { AngularFireAuth } from '@angular/fire/auth';&lt;/code&gt; and that gives us access to the AngularFireAuth object where we can use the methods &lt;code&gt;signInWithEmailAndPassword&lt;/code&gt; to login our user with a username and password. Just keep in mind that with FireBaseAuth you have many options for authentication, so make sure you use the method that you need for your authentication. You can see here the documentation of the available options (&lt;a href="https://firebase.google.com/docs/reference/js/firebase.auth.Auth" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/reference/js/firebase.auth.Auth&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Now that we have our application connected to firebase, let run it and see what happens. Open your console and run &lt;code&gt;ng serve -o&lt;/code&gt; after the application builds, it should open the browser with our application.&lt;/p&gt;

&lt;p&gt;Open the developer console, and navigate to the login page. Once there enter a username and password. Notice the error on the console that error means that we do not have Firebase setup to authenticate users using email and password. To enable that, go to the firebase website and open your application. Now click on Authentication -&amp;gt; sign-in method.&lt;/p&gt;

&lt;p&gt;Now click on Email/Password, select enable and click save. Click on the user tabs and add a new user. Once that is done, go back to your application and enter the username and password you created on the firebase website.&lt;/p&gt;

&lt;p&gt;Notice now that on the developer console you do not see the error message anymore and you can see an object with user information and login type. That means that the user was authenticated.&lt;/p&gt;

&lt;p&gt;Now open &lt;code&gt;home.component.ts&lt;/code&gt; and add the following code. This code will check if the user is logged in or not.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;home.component.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.sass']
})
export class HomeComponent implements OnInit {
  userData = '';

  constructor(private afAuth: AngularFireAuth) { }

  ngOnInit() {
    this.afAuth.authState.subscribe(data =&amp;gt; {
      if (data) {
        this.userData = data.email;
        console.log(data);
      }
    });
  }

  logoutUser() {
    this.afAuth.auth.signOut().then(response =&amp;gt; {
      console.log(response);
      this.userData = '';
    })
    .catch(error =&amp;gt; {
      console.log(error);
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see how our application is able to connect with Firebase Auth and validate the user credentials, also note that if the user closes the browser and comes back to your application it will check with Firebase if the user is already logged in and if they are it will display the welcome message. A few improvements we need to do is create a service for all the Firebase authentication function, so we can reuse the code throughout our application. On the next part, the starter repo will include a new service with all the authentication functions and we will add a check to make sure only registered users can access some routes on our application.&lt;/p&gt;

&lt;p&gt;You can change to the branch &lt;code&gt;Reactive-Form-Part-2-Final&lt;/code&gt; to view all the file changes we did.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>firebase</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How to create a reactive form in Angular</title>
      <dc:creator>Ennio Bozzetti</dc:creator>
      <pubDate>Wed, 10 Jul 2019 12:44:31 +0000</pubDate>
      <link>https://dev.to/enniob/how-to-create-a-reactive-form-in-angular-2c1d</link>
      <guid>https://dev.to/enniob/how-to-create-a-reactive-form-in-angular-2c1d</guid>
      <description>&lt;p&gt;In this article, I will go over how to create an Angular reactive form. We will be making a login form. I will also demonstrate how we can easily add validation to our form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s set up our project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you don’t have Angular already installed on your computer, go to &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular.io&lt;/a&gt; and follow the instructions on how to install Angular on your machine. You will also need an editor. My preferred editor is Visual Studio Code.&lt;/p&gt;

&lt;p&gt;Let’s make a new Angular project. Open a command prompt and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new &amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the steps on the prompt and once that is done you should have a new Angular application.&lt;/p&gt;

&lt;p&gt;To test your new application, open the command prompt and type &lt;code&gt;ng serve&lt;/code&gt;; wait a few seconds and then point your web browser to &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Flocalhost%3A4200%2F" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Flocalhost%3A4200%2F" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwctrimtkfnl4ntt3el3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwctrimtkfnl4ntt3el3.PNG" alt="Sample Angular Application" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s do some coding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your code editor, open &lt;code&gt;app.component.html&lt;/code&gt; and replace all the code there with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div&amp;gt;
    &amp;lt;button [routerLink]="['/']"&amp;gt;Home&amp;lt;/button&amp;gt;
    &amp;lt;button [routerLink]="['login']"&amp;gt;Login&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above source code will add the login button. If you click on the login button you will get the following error: &lt;code&gt;Error: Cannot match any routes. URL Segment: 'login' Error: Cannot match any routes. URL Segment: 'login'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To fix this error we need to make a new component and create a new route. Open your command prompt and enter the following command: &lt;code&gt;ng g component /login&lt;/code&gt; to generate the login component code. Next, open &lt;code&gt;app-routing.modules.ts&lt;/code&gt; file and make a new route called login. Your route should look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';

const routes: Routes = [
  {path: 'login', component: LoginComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;ng serve&lt;/code&gt; and open your browser. If you click on the login button now, you will not get the error message and you should see a message on the page saying login works!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2b48l4wd6ncn3dumg7rg.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2b48l4wd6ncn3dumg7rg.PNG" width="485" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our route working let’s make an Angular reactive form. Open the login.component.ts file and type the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';

import { FormGroup, FormControl, Validator, Validators } from '@angular/forms';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {

  loginForm = new FormGroup({
    userName: new FormControl('', Validators.compose([Validators.required])),
    password: new FormControl('', Validators.compose([Validators.required]))
  });

  constructor() { }

  ngOnInit() {
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have a FormGroup that contains two FormControls, one for the username input and another one for the password input. In our login.component.html we can use the following HTML code to display the form on the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form [formGroup]="loginForm" class="myForm"&amp;gt;
  Username:
  &amp;lt;input type="text" formControlName="userName"&amp;gt;
  Password:
  &amp;lt;input type="password" formControlName="password"&amp;gt;
  &amp;lt;button type="submit"&amp;gt;Login&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser and you should see an error message in your developer console saying that it can’t bind to ‘formGroup’ since it isn’t a known property of ‘form’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrm1cayf9kwr5yolbgab.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrm1cayf9kwr5yolbgab.PNG" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is because we did not include the ReactiveFormModules to our app.module.ts, so add the following code to your app.module.ts file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    BrowserAnimationsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you go back to your browser, you should notice that there are no more errors in the developer console.&lt;/p&gt;

&lt;p&gt;If you click on the login button nothing happens. We will need to create a function and call it when the form is submitted.  Open login.component.htm and add the following to the form tag &lt;code&gt;(ngSubmit)=”doLogin(loginForm)”&lt;/code&gt; In login.component.ts we a function called doLogin that accepts a parameter of type formGroup. Here is the code for the function:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;login.component.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form [formGroup]="loginForm" (ngSubmit)="doLogin(loginForm)" class="myForm"&amp;gt;
  Username:
  &amp;lt;input type="text" formControlName="userName"&amp;gt;
  Password:
  &amp;lt;input type="password" formControlName="password"&amp;gt;
  &amp;lt;button type="submit"&amp;gt;Login&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;login.component.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';

import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {

  loginForm = new FormGroup({
    userName: new FormControl(''),
    password: new FormControl('')
  });

  constructor() { }

  ngOnInit() {
  }

  doLogin(formData: FormGroup) {
    console.log(formData);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you have the developer console open in your browser and click the login button on your form. The console displays the FormGroup output.  Click on the arrow on the left to expand the FormGroup properties.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtyutie4hszt4uuqlc7q.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtyutie4hszt4uuqlc7q.PNG" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our login form working, let’s make it prettier. We will be using Angular Material, so we must first install it. For more information about Angular Material visit their website at &lt;a href="https://material.angular.io" rel="noopener noreferrer"&gt;https://material.angular.io&lt;/a&gt;. Type the following command on the command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng add @angular/material
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the install is done, we can import the theme we want to use. Add the following line to your styles.scss.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import "~@angular/material/prebuilt-themes/indigo-pink.css";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let’s replace the code in the following files:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Login.component.html&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mat-card class="loginCard"&amp;gt;
  &amp;lt;mat-card-content&amp;gt;
    &amp;lt;form [formGroup]="loginForm" (ngSubmit)="doLogin(loginForm)" class="myForm"&amp;gt;
      &amp;lt;mat-form-field class="fullWidth"&amp;gt;
        &amp;lt;mat-label&amp;gt;
          Username:
        &amp;lt;/mat-label&amp;gt;
        &amp;lt;input matInput type="text" formControlName="userName"&amp;gt;
      &amp;lt;/mat-form-field&amp;gt;
      &amp;lt;mat-form-field class="fullWidth"&amp;gt;
        &amp;lt;mat-label&amp;gt;
          Password:
        &amp;lt;/mat-label&amp;gt;
        &amp;lt;input matInput type="password" formControlName="password"&amp;gt;
      &amp;lt;/mat-form-field&amp;gt;
      &amp;lt;button mat-button type="submit"&amp;gt;Login&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  &amp;lt;/mat-card-content&amp;gt;
&amp;lt;/mat-card&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Login.component.scss&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.loginCard {
  width: 400px;
  margin-left: auto;
  margin-right: auto;
}

.myForm{
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.fullWidth {
  width: 100%;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;App.component.html&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mat-toolbar color="primary"&amp;gt;
  &amp;lt;mat-toolbar-row&amp;gt;
    &amp;lt;button mat-button [routerLink]="['/']"&amp;gt;Home&amp;lt;/button&amp;gt;
    &amp;lt;button mat-button [routerLink]="['login']"&amp;gt;Login&amp;lt;/button&amp;gt;
  &amp;lt;/mat-toolbar-row&amp;gt;
&amp;lt;/mat-toolbar&amp;gt;

&amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;App.module.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

// MATERIAL
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    BrowserAnimationsModule,
    MatCardModule,
    MatFormFieldModule,
    MatInputModule,
    MatButtonModule,
    MatToolbarModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build the application using &lt;code&gt;ng serve&lt;/code&gt; and you will see a navigation header with the login form centered on the page.&lt;/p&gt;

&lt;p&gt;Now that we have our login form looking nicer, we can add some form validation to ensure the user enters valid data.&lt;/p&gt;

&lt;p&gt;Add the following code to login.component.ts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';

import { FormGroup, FormControl, Validator, Validators } from '@angular/forms';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {

  loginForm = new FormGroup({
    userName: new FormControl('', Validators.compose([Validators.required])),
    password: new FormControl('', Validators.compose([Validators.required]))
  });

  constructor() { }

  ngOnInit() {
  }

  doLogin(formData: FormGroup) {
    console.log(formData);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you go back to the login page and click the login button, you will notice that the inputs will turn red, and if you open the console window in your browser, you can that the FormGroup valid property is set to invalid. This is because the username and password input was set to be required in the FormGroup.&lt;br&gt;
Go back to the form and enter a username and password and click on login. You will see that the Formgroup valid property is now set to true in your console.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
You now know how to create reactive forms. You could easily add multiple validators if you wanted to and even create your own custom validator. In my next article, I will show you how to create a login form that uses Firebase Authentication and we will also make a registration form that connects to Firebase as well.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Building a Progressive Web App with Angular — Part 2</title>
      <dc:creator>Ennio Bozzetti</dc:creator>
      <pubDate>Wed, 15 May 2019 01:45:15 +0000</pubDate>
      <link>https://dev.to/enniob/building-a-progressive-web-app-with-angular-part-2-2791</link>
      <guid>https://dev.to/enniob/building-a-progressive-web-app-with-angular-part-2-2791</guid>
      <description>&lt;p&gt;This is part 2 of my tutorial on Building a Progressive Web App with Angular. In this tutorial I will explain how to host your web app on Firebase Hosting, and how to notify users when your web app has an update.&lt;/p&gt;

&lt;p&gt;If you do not have an account with Firebase you will need to make one. To do this, visit &lt;a href="https://firebase.google.com/" rel="noopener noreferrer"&gt;https://firebase.google.com/&lt;/a&gt;; click on Sign In and follow the prompts.&lt;/p&gt;

&lt;p&gt;If you already have an account, click on &lt;strong&gt;GO TO CONSOLE&lt;/strong&gt; and it should take you to the &lt;strong&gt;Welcome to Firebase!&lt;/strong&gt; screen. From here you can make a new project or use an existing 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%2Fwgbhrx9z4mvv05bxt0k4.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%2Fwgbhrx9z4mvv05bxt0k4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the command line and type the following command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install -g firebase-tools&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once completed, you should be able to use the &lt;code&gt;firebase&lt;/code&gt; command. To connect your local machine to your Firebase account run the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;firebase login&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should get a few questions on the command prompt and you will be redirected to a Google authentication page where you will need to login and give access.&lt;/p&gt;

&lt;p&gt;Now lets initialize our Firebase project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;firebase init&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will get a series of question. For the purpose of this tutorial we are only interested in the hosting options, so make sure you select: &lt;strong&gt;( ) Hosting: Configure and deploy Firebase Hosting sites&lt;/strong&gt; and press &lt;strong&gt;Enter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next you will be asked if you want to select an existing project or create a new one. Select the one that is appropriate for you. For this tutorial I selected the &lt;strong&gt;Create a new project&lt;/strong&gt; option.&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;Hosting Setup&lt;/strong&gt; section you need to specify the output directory. Since we are using Angular CLI the output files will be located in the dist folder once your project is built. Be sure to select &lt;strong&gt;dist&lt;/strong&gt; for this option.&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%2Fjfc3cosxfqv6l7gpilws.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%2Fjfc3cosxfqv6l7gpilws.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Yes&lt;/strong&gt; for single-page app and that is it. Your project is now setup to use Firebase Hosting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s do some coding
&lt;/h2&gt;

&lt;p&gt;Open VS Code or your preferred editor.&lt;/p&gt;

&lt;p&gt;First let’s change our build directory, open &lt;code&gt;angular.json&lt;/code&gt; file and change the outputPath to be: &lt;code&gt;"outputPath": "dist"&lt;/code&gt; The Angular CLI default is &lt;code&gt;dist/[PROJECTNAME]&lt;/code&gt;, and since we told Firebase CLI that our project will be on the &lt;code&gt;dist&lt;/code&gt; folder only, we need to make a small change on our &lt;code&gt;angular.json&lt;/code&gt; file.&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%2Fgf5zuwi7eziuyvebwzx6.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%2Fgf5zuwi7eziuyvebwzx6.png"&gt;&lt;/a&gt;&lt;br&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%2Fnqb9r3yg8lnjwlm2mkuc.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%2Fnqb9r3yg8lnjwlm2mkuc.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s build our application and upload to Firebase. Open your command line and type the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng build --prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once the application is done, open the &lt;strong&gt;dist&lt;/strong&gt; folder you will see your application files there. To upload the app to Firebase we need to run the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;firebase deploy&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The deploy should take a few seconds and you should be able to see what is happening on the command line. Once it’s done you will get a Hosting URL and if you open that URL in your browser you will see the default Angular application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let’s make some changes to our app
&lt;/h2&gt;

&lt;p&gt;We will make some HTML changes so we can display a welcome message and also notify the user that we have changed the website so they know to reload to view the latest version of our site. We will be using Angular Material to display the alter message, so let’s install Angular Material.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng add @angular/material&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once the installation is completed, we need to add MatSnackBarModule to our &lt;code&gt;app.modules.ts&lt;/code&gt; file.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now let’s open &lt;code&gt;app.component.html&lt;/code&gt; file and change the HTML to display our welcome message. For now we will just have a basic welcome message. Delete all the HTML from the file and just enter the following HTML code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Open &lt;code&gt;app.component.ts&lt;/code&gt; and let’s setup our Snackbar to notify the user once there is a change to the website. To do that we need to import MatSnackBar, and SwUpdate to our component.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The code in the constructor method will execute when the app loads and it will check to see if there are any updates between the server files and the files cached on the service workers. If they are indeed different, a snackbar will display on the screen and prompt the user to reload the application. So let’s try that now that we imported all the required modules and we added the code to check for updates. Let’s build our application again and upload to Firebase.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng build --prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Wait a few seconds and open your Firebase project URL in a new tab. The app will load using the files from the service worker cache, and after it’s done checking if files were updated, you will see the snackbar asking the user to reload the page. Click on “Reload” and you should see the updated app.&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%2F9nu7spznz0f7y50fd9or.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%2F9nu7spznz0f7y50fd9or.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This completes our tutorial. In the next tutorial I will build a PWA app that will use everything that we learned in part 1 and part 2. If you have any questions, comments or suggestion for any other topic you want to see covered, please let me know.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>pwa</category>
      <category>webapp</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a Progressive Web App with Angular — Part 1</title>
      <dc:creator>Ennio Bozzetti</dc:creator>
      <pubDate>Mon, 13 May 2019 11:46:27 +0000</pubDate>
      <link>https://dev.to/enniob/building-a-progressive-web-app-with-angular-part-1-54kb</link>
      <guid>https://dev.to/enniob/building-a-progressive-web-app-with-angular-part-1-54kb</guid>
      <description>&lt;p&gt;If you don’t know what Progressive Web Apps (PWA) are I hope this tutorial will give you an introduction to PWAs and also make you understand how they work and why it’s such a good idea to develop PWA applications.&lt;/p&gt;

&lt;p&gt;I will be using the latest version of &lt;a href="https://angular.io" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; for the purpose of this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is PWA?
&lt;/h2&gt;

&lt;p&gt;A PWA is a web app that uses modern web development technologies to deliver app-like experience to users when they view your website on a mobile device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build a Progressive Web App?
&lt;/h2&gt;

&lt;p&gt;A PWA app gives your website user features that were only available to native apps in the past. Here are some features that are available in a PWA application:&lt;/p&gt;

&lt;p&gt;*Home screen icon&lt;br&gt;
*Offline mode&lt;br&gt;
*Web push notification&lt;br&gt;
*and many more&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href="https://whatwebcando.today/" rel="noopener noreferrer"&gt;link&lt;/a&gt; to a website that gives you a list of features that are currently available for PWA applications.&lt;/p&gt;

&lt;p&gt;I hope this gives you a short and simple overview of PWA and why we should start looking at it. So let’s create our first PWA.&lt;/p&gt;




&lt;p&gt;First let’s install Angular.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install -g @angular/cli&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once this is complete, we can make our new project&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng new my-first-pwa --skip-tests --style scss&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you are new to Angular CLI, here is the &lt;a href="https://angular.io/cli" rel="noopener noreferrer"&gt;documentation link&lt;/a&gt;. I recommend taking a look at it, since there is so much you can do now with the Angular CLI. Either way, here is a brief explanation of the commands used:&lt;/p&gt;

&lt;p&gt;*&lt;code&gt;ng new&lt;/code&gt; Creates a new Angular application&lt;br&gt;
*&lt;code&gt;my-first-pwa&lt;/code&gt; This is the application name, so feel free to change to whatever you want&lt;br&gt;
*&lt;code&gt;--skip-tests&lt;/code&gt; This command will skip the spec files&lt;br&gt;
*&lt;code&gt;--style scss&lt;/code&gt; This will make the style files extension to be scss.&lt;/p&gt;

&lt;p&gt;You now have your Angular application. In order, to preview your application you need to run ng serve -o This will build your application and open it in a browser.&lt;/p&gt;

&lt;p&gt;Here is what it should look like:&lt;/p&gt;

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

&lt;p&gt;Now let’s make our application a PWA. Luckily for us Angular has made it easy to convert an Angular application into a PWA. All we need to do is install a package called &lt;code&gt;@angular/pwa&lt;/code&gt; and this will add all the files and setup your application to be a PWA. So let’s do it!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng add @angular/pwa&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This should take only a few seconds. We can now test our application to see what changed.&lt;/p&gt;

&lt;p&gt;First, we need to build our project&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng build --prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;code&gt;ng server&lt;/code&gt; does not work with service workers, we will need to setup a HTTP server to test our service worker.&lt;/p&gt;

&lt;p&gt;Run the following command to install http-server.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install -g http-server&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once completed, change the directory to your dist folder &lt;code&gt;cd dist/my-first-pwa&lt;/code&gt; and &lt;code&gt;run http-server -p 8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Go to the following URL &lt;code&gt;http://127.0.0.1:8080&lt;/code&gt; on your browser. You should see the default Angular application.&lt;/p&gt;

&lt;p&gt;But how can I check to see if my PWA is working correctly? To do this, open your browser developer tool, click on network and check Offline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F65l5yl2x2i3inz2rhvq9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F65l5yl2x2i3inz2rhvq9.png" title="Enable Offline Mode" alt="alt text" width="763" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now refresh your browser. Notice that on the network tab all the files are being loaded from ServiceWorker, and your website loads with all the content and image. This means that all the resources are being loaded from the service worker’s cache. This is just one of the many benefits of PWAs.&lt;/p&gt;

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

&lt;p&gt;That is it! As you can see, Angular makes it really easy to create a new application and set it up to be a PWA. In the next tutorial I will show you how to configure Firebase so you can host your application, make changes to the application, notify the users of application updates and also update the service workers so that the correct files are loaded when the application is run next.&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href="https://github.com/enniob/1stpwatutorial" rel="noopener noreferrer"&gt;link&lt;/a&gt; to my github repository with the tutorial files.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>pwa</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
