QR Code Event Registration App - Angular PWA
Tags: angular, frontend, pwa, tutorial
Published: 2019-11-20 04:58:39

In this tutorial, I'm going to show you how to create a simple PWA that you can install on your phone. This PWA will be able to create and read QR codes. With this app, you will have more control of your guests for your next event! 

<img width="100px" src="https://thepracticaldev.s3.amazonaws.com/i/4ei3fuju7w10royfb5fm.png" /> 

<img width="100px" src="https://thepracticaldev.s3.amazonaws.com/i/5gew5lvwznkcbvdktey8.png" /> 

In this app, we are going to pretend this is a registration app for your event.

The final code:
https://github.com/devpato/angular-pwa-qrcode

Live App:
https://qr-app-79289.firebaseapp.com/

Let's create an angular app!


1) Run, then when asked to create routing, click yes. Then, when asked to select a CSS processor, select SCSS.

```typescript
ng new qr-app
```

2) Let's create our app into a PWA (Progressive Web App) so we can install the app on our phone. Inside of your project, in your command line run:
```typescript
ng add @angular/pwa
```

3) Create a registration component.
```typescript
ng g c registration
```

4) Create a scanner component.
```typescript
ng g c scanner
```

5) Create a guest-list component.
```typescript
ng g c guest-list
```
6) Create a guest service.
```typescript
ng g s guest
```
7) Create the navbar component.
```typescript
ng g c navbar
```

8) Install the following packages inside of your project:
```typescript
//This is the package that scans QR codes
npm i @zxing/ngx-scanner

//This is the packages that generates QR codes
npm i ngx-qrcode2

//This is the package used to generate a random id for your users
npm i uuid
```

9) Go to your <strong>index.html</strong> and add the Semantic UI CDN to give some styling to our app.

```html
<link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"
      integrity="sha256-9mbkOfVho3ZPXfM7W8sV2SndrGDuh7wuyLjtsWeTI1Q="
      crossorigin="anonymous"
    />
```

10) Navigate to your <strong>registration.component.ts</strong> file then copy/paste the following code:

```typescript
import { FormBuilder } from "@angular/forms";
import { Validators } from "@angular/forms";
import { v4 as uuid } from "uuid";
import { GuestService } from "../guest.service";
import { Router } from "@angular/router";
.
.
.
export class RegistrationComponent implements OnInit {
  registrationForm = this.fb.group({
    firstName: ["", Validators.required],
    lastName: ["", Validators.required]
  });

  constructor(
    private fb: FormBuilder,
    private guestService: GuestService,
    private router: Router
  ) {}

  ngOnInit() {}

  onSubmit(): void {
    const guest = { ...this.registrationForm.value, id: uuid() };
    guest.qr = JSON.stringify(guest);
    this.guestService.addGuest(guest);
    this.registrationForm.reset();
    this.router.navigate(["/guests"]);
  }
}
```

11) Navigate to your <strong>registration.component.scss</strong> file then copy/paste the following code:

```css
.registration {
  text-align: center;
  margin: 8px;
  &-form {
    form {
      display: flex;
      flex-direction: column;

      .field {
        margin: 8px;
      }
    }
  }
}
```

12) Navigate to your <strong>registration.component.html</strong> file then copy/paste the following code. The following code will generate a new guest which will be the info inside of our QR code.

```html
<div class="registration">
  <h2>QR Code Registration App!</h2>
  <span>Please enter your information to register for the event.</span>
  <div class="registration-form">
    <form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
      <div class="ui left corner labeled input field">
        <input
          type="text"
          placeholder="Fist Name"
          formControlName="firstName"
        />
        <div class="ui left corner label">
          <i class="asterisk icon"></i>
        </div>
      </div>
      <div class="ui left corner labeled input field">
        <input type="text" placeholder="Last Name" formControlName="lastName" />
        <div class="ui left corner label">
          <i class="asterisk icon"></i>
        </div>
      </div>
      <button
        class="ui inverted orange button"
        type="submit"
        [disabled]="!registrationForm.valid"
      >
        Registration
      </button>
    </form>
  </div>
</div>

```

13) Navigate to your <strong>navbar.component.html</strong> file then copy/paste the following code:

```html
<div class="ui three item menu">
  <a class="item" [routerLink]="['']">Registration</a>
  <a class="item" [routerLink]="['/scanner']">QR Scanner</a>
  <a class="item" [routerLink]="['/guests']">Guest</a>
</div>
```

14) Navigate to your <strong>guest-list.component.ts</strong> file then copy/paste the following code:

```typescript
import { GuestService } from "../guest.service";
.
.
.
guestList$ = this.guestService.guests$;
  elementType: "url" | "canvas" | "img" = "url";
  constructor(private guestService: GuestService) {}
ngOnInit() {}
```

15) Navigate to your <strong>guest-list.component.html</strong> file then copy/paste the following code:

```html
<div class="guest-list">
  <table class="ui celled table" *ngIf="guestList$ | async as guestList">
    <tbody>
      <tr *ngFor="let g of guestList">
        <td data-label="firstName">{{ g.firstName }}</td>
        <td data-label="lastName">{{ g.lastName }}</td>
        <td data-label="qr">
          <ngx-qrcode
            [qrc-element-type]="elementType"
            [qrc-value]="g.qr"
            class="qrcode"
          ></ngx-qrcode>
        </td>
      </tr>
    </tbody>
  </table>
</div>
```

16) Navigate to your <strong>guest-list.component.scss</strong> file then copy/paste the following code:

```css
.qrcode {
  display: flex;
  justify-content: center;
}

```

17) Navigate to your <strong>scanner.component.ts</strong> file then copy/paste the following code:

```typescript
import { Guest } from "../guest.model";
import { GuestService } from "../guest.service";
import { map } from "rxjs/operators";
.
.
.
availableDevices: MediaDeviceInfo[];
  currentDevice: MediaDeviceInfo = null;
  hasDevices: boolean;
  hasPermission: boolean;
  qrResult: Guest;
  guestExist: boolean;

  constructor(private guestService: GuestService) {}

  ngOnInit(): void {}

  //Clears the QR code scanned
  clearResult(): void {
    this.qrResult = null;
  }

  //Scans the QR code
  onCodeResult(resultString: string): void {
    this.guestExist = null;
    if (this.checkQRJSON(resultString)) {
      this.qrResult = JSON.parse(resultString);
      this.checkInGuest(this.qrResult);
      this.clearMessage();
    } else {
      this.guestExist = false;
      this.clearMessage();
    }
  }

  //Permission for the app to use the device camera
  onHasPermission(has: boolean): void {
    this.hasPermission = has;
  }

  //Checks if the QR code belongs to a valid guest
  checkInGuest(guestQR: Guest): void {
    this.guestService.guests$
      .pipe(
        map(guests =>
          guests.find((guest: Guest) => guest.id === guestQR.id)
        )
      )
      .subscribe(guest => {
        if (guest !== null && guest !== undefined) {
          this.guestExist = true;
        } else {
          this.guestExist = false;
        }
        this.clearResult();
        this.clearMessage();
      });
  }

  clearMessage() {
    setTimeout(() => {
      this.guestExist = null;
    }, 3000);
  }

  //This function check if the QR code has a valid JSON as data
  checkQRJSON(qrString: string): boolean {
    if (
      /^[\],:{}\s]*$/.test(
        qrString
          .replace(/\\["\\\/bfnrtu]/g, "@")
          .replace(
            /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
            "]"
          )
          .replace(/(?:^|:|,)(?:\s*\[)+/g, "")
      )
    ) {
      return true;
    } else {
      return false;
    }
  }
```

18) Navigate to your <strong>scanner.component.html</strong> file then copy/paste the following code:

```html
<div class="qr-scan-area">
  <!-- This is the NPM package in-charge of scanning the QR -->
  <zxing-scanner
    #scanner
    [(device)]="currentDevice"
    (scanSuccess)="onCodeResult($event)"
    (permissionResponse)="onHasPermission($event)"
  ></zxing-scanner>
  <div class="qr-area">
    <div class="area"></div>
  </div>
</div>

<!-- Displays message on the screen if guest is valid or not -->
<div class="guest">
  <ng-container *ngIf="guestExist">
    <div class="ui success message">
      <i class="close icon"></i>
      <div class="header">
        Welcome!!
      </div>
      <p>Guest has been found on the guest lists</p>
    </div>
  </ng-container>
  <ng-container #notFound *ngIf="guestExist === false">
    <div class="ui negative message">
      <i class="close icon"></i>
      <div class="header">
        Warning!
      </div>
      <p>This person is not a guest!</p>
    </div>
  </ng-container>
</div>
```

19) Navigate to your <strong>scanner.component.scss</strong> file then copy/paste the following code. In this CSS, we are drawing a red square to make the scanner look cooler but it is not needed for the scanner to work.

```css
::ng-deep {
  .qr-scan-area {
    position: relative;
    zxing-scanner {
      max-width: 100%;
    }

    .qr-area {
      position: absolute;
      display: flex;
      justify-content: center;
      align-items: center;
      height: calc(100% - 50px);
      top: 0;
      width: 100%;
      .area {
        height: 200px;
        width: 200px;
        border: 2px solid red;
      }
    }
  }
}
```

20) Inside of the app folder, create a new interface with the following name: <strong>guest.model.ts</strong>

21) Inside of your <strong>guest.model.ts</strong> copy/paste the following:

```typescript
export interface Guest {
  id: string;
  firstName: string;
  lastName: string;
  qr?: string;
}

```

22) Now, navigate to the guest service you previously created. <strong> guest.service.ts</strong>. The following code is the code that will handle populated dummy data to start with as well as adding every new guest created to the array of guest.

```typescript
import { Guest } from "./guest.model";
import { BehaviorSubject } from 'rxjs';
.
.
.
private guestSource = new BehaviorSubject<Guest[]>(null);
  guests$ = this.guestSource.asObservable();
  private guests = [
    {
      id: "7558e6e5-3cfa-4c24-b5b7-653ecbd49925",
      firstName: "Pato",
      lastName: "Vargas"
    },
    {
      id: "4847498c-b57f-4ceb-8c0c-8831b9972158",
      firstName: "Diego",
      lastName: "Maradona"
    }
  ];

  constructor() {
    this.populateQR();
    this.guestSource.next(this.guests);
  }

  populateQR(): void {
    this.guests.forEach((g: Guest) => (g.qr = JSON.stringify({ ...g })));
  }

  addGuest(newGuest: Guest): void {
    this.guests.push(newGuest);
    this.guestSource.next(this.guests);
  }
```

23) Go to your app.module.ts and add the following modules to your code:

```typescript
import { NgxQRCodeModule } from "ngx-qrcode2";
import { ZXingScannerModule } from "@zxing/ngx-scanner";
import { ReactiveFormsModule } from "@angular/forms";
.
.
.
imports: [
    BrowserModule,,
    AppRoutingModule,
    ServiceWorkerModule.register("ngsw-worker.js", {
      enabled: environment.production
    }),
    ReactiveFormsModule,
    ZXingScannerModule,
    NgxQRCodeModule
]
```

24) Finally, we need to create the routes for our app. Copy and paste the following code:

```typescript
import { RegistrationComponent } from "./registration/registration.component";
import { GuestListComponent } from "./guest-list/guest-list.component";
import { ScannerComponent } from "./scanner/scanner.component";

const routes: Routes = [
  {
    path: "",
    component: RegistrationComponent
  },
  {
    path: "guests",
    component: GuestListComponent
  },
  {
    path: "scanner",
    component: ScannerComponent
  }
];
```

25) Go to your <strong>app.component.html</strong> and erase everything that is in there and copy/paste the following:

```html
<app-navbar></app-navbar>
<router-outlet></router-outlet>
```

26) Time to test our app. Run the app with ng serve --o. You should see the following:

<div style="display:flex;">
 <img width="200px" src="https://thepracticaldev.s3.amazonaws.com/i/hp2nmaq2c9olo7h3b03n.png" />
</div>

<h2>Time to use our PWA in our phone.</h2>

1) First run:
```typescript 
ng build --prod
```

2) Deploy your app to your favorite hosting provider. I like Firebase. It's very easy to deploy. (I'm not going to show you how to do so, so please google it).

3) Once your app has been deployed, go to your phone and navigate to the URL where your app is living.

4) Once the app opens, it will ask you if you want to add your app to your home screen because it is a PWA; like the picture below shows.

<img width="200px" src="https://thepracticaldev.s3.amazonaws.com/i/e89xur2x2abqqkim8ylf.png" />

<img width="200px" src="https://thepracticaldev.s3.amazonaws.com/i/a9y6i4x3m61zx8ul16jh.png" />

<img width="200px" src="https://thepracticaldev.s3.amazonaws.com/i/1zxigz9k2l4cldo68a5g.png" />


<img src="https://thepracticaldev.s3.amazonaws.com/i/5a87eog40zvcsimuaew4.png" />