DEV Community

Cover image for QR Code Event Registration App - Angular PWA
Pato
Pato

Posted on • Updated on

QR Code Event Registration App - Angular PWA

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!

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.

ng new qr-app
Enter fullscreen mode Exit fullscreen mode

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:

ng add @angular/pwa
Enter fullscreen mode Exit fullscreen mode

3) Create a registration component.

ng g c registration
Enter fullscreen mode Exit fullscreen mode

4) Create a scanner component.

ng g c scanner
Enter fullscreen mode Exit fullscreen mode

5) Create a guest-list component.

ng g c guest-list
Enter fullscreen mode Exit fullscreen mode

6) Create a guest service.

ng g s guest
Enter fullscreen mode Exit fullscreen mode

7) Create the navbar component.

ng g c navbar
Enter fullscreen mode Exit fullscreen mode

8) Install the following packages inside of your project:

//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
Enter fullscreen mode Exit fullscreen mode

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

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

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

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"]);
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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

      .field {
        margin: 8px;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

12) Navigate to your registration.component.html 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.

<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>

Enter fullscreen mode Exit fullscreen mode

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

<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>
Enter fullscreen mode Exit fullscreen mode

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

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

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

<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>
Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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

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;
    }
  }
Enter fullscreen mode Exit fullscreen mode

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

<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>
Enter fullscreen mode Exit fullscreen mode

19) Navigate to your scanner.component.scss 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.

::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;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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

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

Enter fullscreen mode Exit fullscreen mode

22) Now, navigate to the guest service you previously created. guest.service.ts. 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.

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);
  }
Enter fullscreen mode Exit fullscreen mode

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

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
]
Enter fullscreen mode Exit fullscreen mode

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

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
  }
];
Enter fullscreen mode Exit fullscreen mode

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

<app-navbar></app-navbar>
<router-outlet></router-outlet>
Enter fullscreen mode Exit fullscreen mode

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

Time to use our PWA in our phone.

1) First run:

ng build --prod
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (0)