Application Development

Let's create an application using Form.io!

The most advanced way to use Form.io is to embed the Form.io renderer directly within any Web based application you are developing. The Form.io platform supports a large number of different application development frameworks such as the following.

Each one of these landing pages provides its own documentation on how to create new applications using these frameworks. For this tutorial, however, we will pick one (Angular) and demonstrate how an application can be created using this powerful platform. For this tutorial, we will build an application that demonstrates many of the different features of the Form.io platform; An Event Registration Application using the Angular framework.

Let's get started by Creating a new project.

Create your Project

After you create a new account at https://portal.form.io, you will see a page that looks like the image below. The first thing we will do is to create a new Project, which we can do by clicking the Angular button, or by clicking on the Create Project button.

Provide your project information like so, and then click Create Project

Create Resources

An Event Registration application will need to store some “structured” objects called Events. These are called Resources in Form.io, so let's create a new Event by clicking on the following.

We will then build our Event object using a form builder. We could also provide some field validations and such, but for now we will keep the form simple.

When you are done, click on Create Resource. This immediately demonstrates one of the differentiating features of Form.io, where Data Models and Resources can be constructed using a simple drag-and-drop form builder interface. We can even control who has access to create this Event within the Access section.

Assigning Permissions

You now need to allow Authenticated users to read all events. We will also want them to be able to create their own, update their own, and delete their own events. To do this, click on Access section, and configure the settings as follows.

The settings should automatically save when you add this role to these permissions.

Event Registration Form

Now that we have an Event resource, we need a way for people to register to attend the Event. This is a great use case for a Form that provides “unstructured” data that references Resources.

Let’s create a new form by clicking on the Forms section and pressing New Form, and then selecting API Web Form.

Here we will build a Registration form. The first field we will add to this form is a Select component field like so…

Once we add this, field, we will give it a label of Event.

We will then click on the Data tab, and then set the Data Source to Resource, and then select the Event Resource like so.

Finally for the Item Template, we will provide the following code.

<span>{{ item.data.title }}</span>

Like the following shows.

Next, we can press the Save button to add the field to our form. Now that we have an Event resource field, we can add other fields to the registration form like so…

We will now press the Create Form button to save our form. Now, let us configure the access permissions of this form.

Access Permissions

We also need to ensure that Authenticated users can submit the Registration Form, so we will go to the Access section and configure the following.

The permissions should automatically save when you set them.

We are now done setting up our Form.io Resources + Forms for our application, now let's build an app!

Create an Application

To get started quickly in Angular, we recommend using the Angular CLI tool to create our application. We can do this by typing the following within your terminal.

npm install -g @angular/cli
ng new eventmanager --style=scss --routing=true --strict=false

This will now create a new application within the folder eventmananger, we can navigate into that folder by typing the following in the terminal.

cd eventmanager

We will now bring in all of our dependencies into the application by typing the following within the application folder.

npm install --save bootstrap@4 bootstrap-icons formiojs @angular/elements ngx-bootstrap --legacy-peer-deps;
npm install --save @formio/angular --tag=rc --legacy-peer-deps

Or if you are using yarn, you can instead run the following

yarn add bootstrap@4 bootstrap-icons formiojs @angular/elements ngx-bootstrap;
yarn add @formio/angular@rc

We can now setup the styles for our application by editing the following file.

src/styles.scss

@import "bootstrap/scss/bootstrap.scss";
@import "bootstrap-icons/font/bootstrap-icons.scss";

We will now start our application by typing the following in the terminal.

ng serve

You should now be able to go to http://localhost:4200 in your browser to see the default Angular page.

We can now create a home page, by typing the following in the terminal.

ng g component home

And then provide the following code in the following file.

src/app/home/home.component.html

<div class="jumbotron">
  <h3>Event Registration Application</h3>
  <p>This is an example home page for the Event Registration Application</p>
</div>

And then add the following to the routes.

src/app/app-routing.module.ts

...
import { HomeComponent } from './home/home.component';

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

...

Next, we will need to make sure that our root component, which is called AppComponent is able to display the content at this route. To do this, we will need to add a router-outlet to the template of this component. You can do this by opening up the following file and changing it to only have the following content.

src/app/app.component.html

<div class="container pt-3">
  <router-outlet></router-outlet>
</div>

Now, when you refresh you should see your home component as follows.

Application Configuration

Now that we have an application running, we will need create a configuration file so that we can tell the application which Form.io project we are pointed to.

To do this, we will create a new config.ts file as follows.

src/app/config.ts

export const AppConfig = {
  appUrl: 'https://yourproject.form.io',
  apiUrl: 'https://api.form.io'
};

You will need to make sure that you replace “yourproject” with the name of your Project URL which you can find at the following location within your Project page.

Next, we will need to register this AppConfig within the main module by adding the following to the app.module.ts file.

src/app/app.module.ts

...
...
import { AppConfig } from './config';
import { FormioAppConfig } from '@formio/angular';

@NgModule({
  ...
  providers: [
    {provide: FormioAppConfig, useValue: AppConfig},
  ],
  ...
})
export class AppModule { }

User Authentication

Now that we have the configuration file in place, we can setup User Authentication.

Angular Auth Documentation

We accomplish this by first creating an Angular module that will contain the authentication routes.

ng g module auth

This will create a new folder within our application within the src/app folder, which we can then use to include all the necessary modules to bring in Authentication.

src/app/auth/auth.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { FormioAuth, FormioAuthRoutes } from '@formio/angular/auth';


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

We must now register the Authentication module by lazy loading the module within the “auth” route. We can accomplish this within the following file.

src/app/app-routing.module.ts

const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'auth',
    loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule)
  }
];

Next, we just need to register the service providers with configurations within the app.module.ts file.

src/app/app.module.ts

...
import { AppConfig } from './config';
import { FormioAppConfig } from '@formio/angular';
import { FormioResources } from '@formio/angular/resource';
import { FormioAuthService, FormioAuthConfig } from '@formio/angular/auth';

@NgModule({
  ...
  providers: [
    {provide: FormioAppConfig, useValue: AppConfig},
    FormioResources,
    FormioAuthService,
    {provide: FormioAuthConfig, useValue: {
      login: {
        form: 'user/login'
      },
      register: {
        form: 'user/register'
      }
    }}
  ],
  ...

We should now add a header to our application so that we can login, and see our login state.

We can now add a header to our application by adding the nav HTML to our existing router-outlet code we created earlier within the app.component.html file. It should look like the following.

src/app/app.component.html

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container">
    <a class="navbar-brand" href="#"><img style="height: 2em;" src="https://portal.form.io/images/formio-logo.png" /></a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarsExample07" aria-controls="navbarsExample07" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarsExample07">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
        <li class="nav-item">
          <a class="nav-link active" aria-current="page" href="#"><i class="bi-house-fill"></i></a>
        </li>
      </ul>
      <ul class="nav navbar-nav ml-auto">
        <li class="nav-item" routerLinkActive="active" *ngIf="!auth.authenticated">
          <a class="nav-link" routerLink="auth">Login | Register</a>
        </li>
        <li class="nav-item" *ngIf="auth.authenticated">
          <a class="nav-link" routerLink="/" (click)="auth.logout()"><span class="glyphicon glyphicon-off"></span> Logout</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
<div class="container pt-3">
  <router-outlet></router-outlet>
</div>

Next, we will need to add some logic within the AppComponent class to handle the authentication workflow.

src/app/app.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { FormioAuthService } from '@formio/angular/auth';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'eventmanager';
  constructor(
    public auth: FormioAuthService,
    private router: Router
  ) {
    this.auth.onLogin.subscribe(() => {
      this.router.navigate(['/']);
    });

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

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

We should now see the following within our application with a working authentication and registration system.

Application Resources

Now that you have a running application with authentication, we can now add the Event resource using the FormioResource module.

Angular Resource Documentation

ng g module event

We will then add the following to the following module file.

src/app/event/event.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import {
  FormioResource,
  FormioResourceRoutes,
  FormioResourceConfig,
  FormioResourceService
} from '@formio/angular/resource';

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

The next thing we will need to do is to register this event within the router.

src/app/app-routing.module.ts

const routes: Routes = [
  ...
  ...
  {
    path: 'event',
    loadChildren: () => import('./event/event.module').then(m => m.EventModule)
  }
];

Finally, you will add a link to the navigation bar to go to the Events section when you click it.

src/app/app.component.html

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container">
    <a class="navbar-brand" href="#"><img style="height: 2em;" src="https://portal.form.io/images/formio-logo.png" /></a>
    <ul class="nav navbar-nav mr-auto">
      ...
      <li class="nav-item" routerLinkActive="event" *ngIf="auth.authenticated">
        <a class="nav-link" routerLink="event"><i class="bi-calendar"></i> Events</a>
      </li>
    </ul>
    ...
  </div>
</nav>
...

You now have a complete Event management system!

Let’s add our Registration form!

Event Registration

We will now add the Event Registration form to the event. Since forms and resources are conceptually the same thing in Form.io, we can use a nested resource to attach forms to any resource within our application. We can start by adding a submodule within Angular.

ng g module event/register

We can now add the following to the created file.

src/app/event/register/register.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import {
  FormioResource,
  FormioResourceRoutes,
  FormioResourceConfig,
  FormioResourceService
} from '@formio/angular/resource';

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    FormioResource,
    RouterModule.forChild(FormioResourceRoutes())
  ],
  providers: [
    FormioResourceService,
    {provide: FormioResourceConfig, useValue: {
      name: 'registration',
      form: 'registration',
      parents: [
        'event',
        {
          field: 'user',
          resource: 'currentUser',
          filter: false
        }
      ]
    }}
  ]
})
export class RegisterModule { }

Notice that we added the parents construct to our providers. This tells the Form.io Angular Resource system that this is nested within the Event resource as well as should contain the currently logged in User assigned to the new records as well.

Next, we need to mount this nested resource within the Event resource. To do this, we add it to the children of the Event resource routes as follows.

src/app/event/event.module.ts

...
import { RouterModule, Routes } from '@angular/router';
...

const eventRoutes: Routes = FormioResourceRoutes();
eventRoutes[2].children.push({
  path: 'registrations',
  loadChildren: () => import('./register/register.module').then(m => m.RegisterModule)
});

@NgModule({
  declarations: [],
  imports: [
    ...
    RouterModule.forChild(eventRoutes)
  ],
  ...
})
export class EventModule { }

We can now navigate to the following url to see the Forms for a specific event, as well as all the registrations within that Event.

http://localhost:4200/event/[EVENTID]/registrations

Next, we will need to change the Event resource view so that we can add the tab to the interface.

ng g component event/resource

We can now add the following the the event resource html.

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

<ul class="nav nav-tabs" style="margin-bottom: 10px;">
  <li class="nav-item"><a class="nav-link" routerLink="../"><i class="bi-chevron-left"></i></a></li>
  <li class="nav-item"><a class="nav-link" routerLink="view" routerLinkActive="active">View</a></li>
  <li class="nav-item"><a class="nav-link" routerLink="registrations" routerLinkActive="active">Registrations</a></li>
  <li class="nav-item"><a class="nav-link" routerLink="edit" routerLinkActive="active">Edit</a></li>
  <li class="nav-item"><a class="nav-link" routerLink="delete" routerLinkActive="active"><span class="bi-trash"></span></a></li>
</ul>
<router-outlet></router-outlet>

Next, we will need to extend the FormioResourceComponent

src/app/event/resource/resource.component.ts

import { Component } from '@angular/core';
import { FormioResourceComponent } from '@formio/angular/resource';

@Component({
  selector: 'app-resource',
  templateUrl: './resource.component.html',
  styleUrls: ['./resource.component.scss']
})
export class ResourceComponent extends FormioResourceComponent {}

And finally, we can register this within the routes using the following.

src/app/event/event.module.ts

const eventRoutes: Routes = FormioResourceRoutes({
  resource: ResourceComponent
});

This should create the following interface, where we can now view all of the registrations within an Event.

Let’s now change the Event view page to add a Register for this Event button!

Event View

To change the view of the Event, we can use the same method we did for the Resource component, but this time we will create one for the event view.

ng g component event/view

We can now modify the following template with the following.

src/app/event/view/view.component.html

<div class="row">
  <div class="col col-sm-6">
    <div class="card">
      <div class="card-header bg-primary text-white">
        <h3 class="card-title">Event Information</h3>
      </div>
      <ul class="list-group list-group-flush">
        <li class="list-group-item"><strong>Title:</strong> {{ service.resource.data.title }}</li>
        <li class="list-group-item"><strong>Description:</strong> {{ service.resource.data.description }}</li>
        <li class="list-group-item"><strong>Date:</strong> {{ service.resource.data.date }}</li>
      </ul>
    </div>
  </div>
  <div class="col col-sm-6">
    <div class="card">
      <div class="card-header bg-success text-white">
        <h3 class="card-title">Event Registration</h3>
      </div>
      <div class="card-body">
        <a routerLink="../registrations/new" class="btn btn-primary">Register for this Event</a>
      </div>
    </div>
  </div>
</div>

Next, we need to extend the FormioResourceView as follows.

src/app/event/view/view.component.ts

import { Component } from '@angular/core';
import { FormioResourceViewComponent } from '@formio/angular/resource';

@Component({
  selector: 'app-view',
  templateUrl: './view.component.html',
  styleUrls: ['./view.component.scss']
})
export class ViewComponent extends FormioResourceViewComponent {}

And finally, we will register this with the FormioResourceRoutes as follows.

src/app/event/event.module.ts

const eventRoutes: Routes = FormioResourceRoutes({
  resource: ResourceComponent,
  view: ViewComponent
});

This produces the following result…

We now have a full working Event Management system, which even allows for other users to create an account and Register for this event. We hope that this walkthrough guide provides you a solid footing on building your next Progressive Web Application on Form.io!

If you would like to download the code behind this application, then go to Event Manager Github Page.

Last updated