Before we start

http://travistidwell.com/presentations/eventmanager


						npm install -g @angular/cli
						ng new eventmanager --style=scss
						cd eventmanager
						npm install --save bootstrap
						npm install --save bootswatch
						npm install --save font-awesome
						npm install --save angular-formio
					

Build an Event Registration System

with Angular 5, Bootstrap 4, and Form.io

by Travis Tidwell | https://form.io
@softwaregnome | travist

Follow along @

http://travistidwell.com/presentations/eventmanager

Installing Angular CLI


						npm install -g @angular/cli
					

Create a new application


						ng new eventmanager --style=scss
						cd eventmanager
						ng serve
					

Add Globals to polyfills.ts

/src/polyfills.ts (end of file)
						(window as any).global = window;
					

Add Bootstrap & Bootswatch


						npm install --save bootstrap
						npm install --save bootswatch
					
src/styles.scss
@import "~bootswatch/dist/yeti/_variables.scss";
@import "~bootstrap/scss/bootstrap.scss";
@import "~bootswatch/dist/yeti/_bootswatch.scss";
$fa-font-path: '../node_modules/font-awesome/fonts';
@import '~font-awesome/scss/font-awesome';
					

Creating a home page


						ng g component home
					
/src/app/home/home.component.html

Welcome to the Event Manager!

Adding routes for home page

/src/app/app.module.ts
...
import { RouterModule } from '@angular/router';
...
@NgModule({
  ...
  imports: [
    ...
    RouterModule.forRoot([
      {
        path: '',
        redirectTo: '/home',
        pathMatch: 'full'
      },
      {
        path: 'home',
        component: HomeComponent
      }
    ])
  ],
  ...
})
					

Create main page content

/src/app/app.component.html

Adding


						npm install --save angular-formio
					

Create a New Project on Form.io

User Authentication

Create a Configuration File

/src/config.ts
import { FormioAppConfig } from 'angular-formio';
import { FormioAuthConfig } from 'angular-formio/auth';

export const AppConfig: FormioAppConfig = {
  appUrl: 'https://[YOUR_PROJECT].form.io',
  apiUrl: 'https://api.form.io',
  icons: 'fontawesome'
};

export const AuthConfig: FormioAuthConfig = {
  login: {
    form: 'user/login'
  },
  register: {
    form: 'user/register'
  }
};

					

Create the Authentication Module


						ng g module auth
					
/src/app/auth/auth.module.ts
...
import { RouterModule } from '@angular/router';
import { FormioAuth, FormioAuthRoutes } from 'angular-formio/auth';

@NgModule({
  imports: [
    CommonModule,
    FormioAuth,
    RouterModule.forChild(FormioAuthRoutes())
  ],
  declarations: []
})
export class AuthModule { }
					

Include the Auth Service + Auth Module

/src/app/app.module.ts
...
import { FormioAppConfig } from 'angular-formio';
import { FormioAuthService, FormioAuthConfig } from 'angular-formio/auth';
import { AuthConfig, AppConfig } from '../config';
...

@NgModule({
  ...
  imports: [
    ...
    RouterModule.forRoot([
	  ...
      {
        path: 'auth',
        loadChildren: './auth/auth.module#AuthModule'
      }
    ])
  ],
  providers: [
    FormioAuthService,
    {provide: FormioAuthConfig, useValue: AuthConfig},
    {provide: FormioAppConfig, useValue: AppConfig}
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
					

Adding Header Component


						ng g component header
					

Adding Navigation Bar

src/app/header/header.component.html

					
src/app/header/header.component.ts
import { FormioAuthService } from 'angular-formio/auth';
...
export class HeaderComponent implements OnInit {

  constructor(private auth: FormioAuthService) { }
  ...
}
					

Include Header Component

/src/app/app.component.html

Redirect to Home after login

src/app/app.component.ts
import { Router } from '@angular/router';
import { FormioAuthService } from 'angular-formio/auth';

...
export class AppComponent {
  ...
  constructor(private auth: FormioAuthService, private router: Router) {
    this.auth.onLogin.subscribe(() => {
      this.router.navigate(['/home']);
    });

    this.auth.onLogout.subscribe(() => {
      this.router.navigate(['/auth/login']);
    });

    this.auth.onRegister.subscribe(() => {
      this.router.navigate(['/home']);
    });
  }
}

					

Event Resource


						ng g module event
					

Create Event Form.io Resource

Add the Resource Providers

/src/app/event/event.module.ts
...
import { RouterModule, Routes } from '@angular/router';
import { FormioResource, FormioResourceRoutes, FormioResourceConfig, FormioResourceService } from 'angular-formio/resource';

@NgModule({
  imports: [
    ...
    FormioResource,
    RouterModule.forChild(FormioResourceRoutes())
  ],
  ...
  providers: [
    FormioResourceService,
    {provide: FormioResourceConfig, useValue: {
      name: 'event',
      form: 'event'
    }}
  ]
})
export class EventModule {}
					

Mount the Resource to the routes

/src/app/app.module.ts
...
import { EventModule } from './event/event.module';
...

@NgModule({
  ...
  imports: [
    ...
    RouterModule.forRoot([
      ...
      {
        path: 'event',
        loadChildren: () => EventModule
      }
    ])
  ],
  ...
})
					

Add a link to the header

/src/app/header/header.component.html

					

We now have all of the available Routes!

  • event - Index of events
  • event/new - Create a new event
  • event/:id/view - View an event
  • event/:id/edit - Edit an event
  • event/:id/delete - Delete an event

Event Participant Resource


						ng g module event/participant
					

Create Registration Form

Create Participant Resource

Add the Resource Providers

/src/app/event/participant/participant.module.ts
...
import { RouterModule } from '@angular/router';
import { FormioResource, FormioResourceRoutes, FormioResourceConfig, FormioResourceService } from 'angular-formio/resource';

@NgModule({
  imports: [
    ...
    FormioResource,
    RouterModule.forChild(FormioResourceRoutes())
  ],
  ...
  providers: [
    FormioResourceService,
    {provide: FormioResourceConfig, useValue: {
      name: 'participant',
      form: 'participant',
      parents: ['event']
    }}
  ]
})
export class ParticipantModule {}
					

Include the FormioResources service

/src/app/app.module.ts
...
import { FormioResources } from 'angular-formio/resource';

...
...
@NgModule({
  ...
  ...
  providers: [
    FormioResources,
    ...
  ],
  ...
})
					

Include in the Event routes

/src/app/event/event.module.ts
import { ParticipantModule } from './participant/participant.module';

const eventResourceRoutes: Routes = FormioResourceRoutes({});

eventResourceRoutes[2].children.push({
  path: 'participant',
  loadChildren: () => ParticipantModule
});

@NgModule({
  imports: [
    ...
    RouterModule.forChild(eventResourceRoutes)
  ],
  ...
})
					

Extend the Event Resource view


						ng g component event/event-resource
					

Extend the ResourceComponent

/src/app/event/event-resource/event-resource.component.ts
...
import { ActivatedRoute } from '@angular/router';
import { FormioResourceService, FormioResourceComponent } from 'angular-formio/resource';

@Component({
  ...
})
export class EventResourceComponent extends FormioResourceComponent implements OnInit {
  constructor(public service: FormioResourceService, public route: ActivatedRoute) {
    super(service, route);
  }

  ngOnInit() {
    super.ngOnInit();
  }
}
					

Edit the Event Resource View

/src/app/event/event-resource/event-resource.component.html


					

Create custom Event view


						ng g component event/event-view
					

Extend the ResourceViewComponent

/src/app/event/event-view/event-view.component.ts
...
import { FormioResourceConfig, FormioResourceService, FormioResourceViewComponent } from 'angular-formio/resource';

@Component({
  ...
})
export class EventViewComponent extends FormioResourceViewComponent implements OnInit {
  constructor(service: FormioResourceService, config: FormioResourceConfig) {
    super(service, config);
  }

  ...
}
					

Edit the Event View Template

/src/app/event/event-view/event-view.component.html
{{ service.resource?.data.start | date }} to {{ service.resource?.data.end | date }}

{{ service.resource?.data.title }}

Registration

Register the extended views with FormioResourceRoutes

/src/app/event/event.module.ts
const eventResourceRoutes: Routes = FormioResourceRoutes({
  view: EventViewComponent,
  resource: EventResourceComponent
});
					

We have an Event Registration System!

Advanced Enhancements

Dynamic Registration Form


						ng g component event/participant/participant-create
					
/src/app/event/participant/participant.module.ts
RouterModule.forChild(FormioResourceRoutes({
  create: ParticipantCreateComponent
}))
					

Dynamic Registration Form

/src/app/event/participant/participant-create/participant-create.component.html

| New {{ service.form.title }}

Dynamic Registration Form

/src/app/event/participant/participant-create/participant-create.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import {
  FormioResourceCreateComponent,
  FormioResourceService,
  FormioResourceConfig
} from 'angular-formio/resource';

const FormioUtils = require('formiojs/utils');

@Component({
  selector: 'app-participant-create',
  templateUrl: './participant-create.component.html',
  styleUrls: ['./participant-create.component.scss']
})
export class ParticipantCreateComponent extends FormioResourceCreateComponent implements OnInit {
  constructor(
    public service: FormioResourceService,
    public route: ActivatedRoute,
    public router: Router,
    public config: FormioResourceConfig
  ) {
    super(service, route, router, config);
  }

  ngOnInit() {
    super.ngOnInit();

    // Wait for the parent event to be loaded.
    this.service.resources['event'].resourceLoaded.then((event) => {

      // Wait for the participant form to load.
      this.service.formLoaded.then((form) => {

        // If they wish to have a custom registration form.
        if (event.data.registrationForm) {
          const registerForm = FormioUtils.getComponent(form.components, 'registration', true);
          registerForm.src = this.service.formFormio.projectUrl + '/' + event.data.registrationForm;
        }
      });
    });
  }
}
					

Auto User Data Population

/src/app/event/participant/participant-create/participant-create.component.ts
...
import { FormioAuthService } from 'angular-formio/auth';

@Component({
  ...
})
export class ParticipantCreateComponent extends FormioResourceCreateComponent implements OnInit {
  constructor(
    ...,
    public auth: FormioAuthService
  ) {
    super(service, route, router, config);
  }

  ngOnInit() {
    this.service.resources['event'].resourceLoaded.then((event) => {
      this.service.formLoaded.then((form) => {
        ...

        // Wait for the current user to be loaded.
        this.auth.userReady.then((user) => {

          // Default the user data inside of the registration form.
          this.service.resource.data.registration = {data: user.data};

          // Tell our form to re-render the submission.
          this.service.refresh.emit({
            property: 'submission',
            value: this.service.resource
          });
        });
      });
    });
  }
					

Thank you!