Reactive Forms | Angular + Loopback


A Model Driven Approach

PRE-CONDITIONS: Complete previous blogs:

Building Web Applications with Angular 2
Building Components using Angular CLI
Setting up MySQL on Your Local Environment
Angular 2 + Loopback Project Setup

Code Resources:
Reactive Form + Loopback
Reactive Form Only

Overview of Tutorial Steps:

Reactive Forms Using FormBuilder
Connecting Loopback with a Angular client using the Loopback SDK
Calling Our Loopback APIs within Angular

If you are working with the project that I shared above please note that MANY things have already been done for you. Some manual, but most with the Loopback and Angular CLIs. Some of these I will not be able to cover within this blog or they are already covered. I will do my best to create blogs on a weekly basis to help bring the gaps of these huge concepts we are learning.

To make it even easier on ourselves, we can utilize some HTML and CSS from Bootstrap to create our form. I would recommended exploring what Bootstrap has to offer, which I am sure you will be amazed. We can update our index.html file so we are importing Bootstrap 4, and its dependencies. Update src/index.html so that it includes the link tag for the bootstrap css and the three script tags to handle jQuery, Tether, and Bootstrap JS dependencies. If we navigate to the browser, you should notice that the text looks a little different. Essentially we now can use all the amazing things bootstrap gives us. If this is confusing at all, please view the github repo for this lesson.


Technical Overview

Our objective is to handle the data flow from our reactive Angular ContactSubmissionForm and our Loopback ContactSubmission model.

One of the many reasons a company may choose to use Angular is due it's powerful form features. Recently, Angular released the new reactive forms, which approached forms in an object-oriented way. Viewing Angular forms from an abstract view, we can see reactive forms entire design just from the image below:


Removing The Complexity

Abstract Control:

We can describe the Abtract Control as an abstract class which has the purpose of representing the FormControl. In other words, it tracks the FormControl state, such as being valid or not, or the value of everything within one abstract class.

FormControl, FormGroup, and FormArray:

The Abstract Class gets extended by the FormControl class, which represents a single control. It can also be extended as two other classes, FormGroup and FormArray, which represent two data structures that we can put the FormControl inside of.


Getting Started

All we need to do is add the ReactiveFormsModule within our app module to begin implementing reactive forms.


Model Driven:

Part of the rationale of the "reactive" forms name is due to how most of the work done when creating them is happening on the code side rather than the HTML form itself. For this, we refer to reactive forms as being model driven. One of the first things that we do when creating reactive forms is to determine the form's shape, which we can describe with an interface.

I hope that you have already asked yourself about how this applies to our Loopback models, or even if that's possible. If we think back to the creation of our Loopback SDK, we showed how the sdk describes the models and methods that each have. Instead of building an interface for the form's data model, we can simply use our model's interface defined within our sdk. This will be easily seen by the ContactSubmissionApi service and it's interface.

First, let's start with creating our form and come back to this thought after we have our Angular side all wrapped up.


Creating our Form with FormBuilder:

Our steps to create a form comprised of the following:
  1. Create a public contactSubmissionForm with the type FormGroup, since a form is literally just a group of controls.

  2. Inside the component's constructor we can create the contactSubmissionForm and instantiate it from a helper class FormBuilder, which makes this a lot easier when comparing the optional FormGroup constructor.

    However, please note that I am purposely not going to explain using the FormGroup constructor. Very quickly this route can cause our form creation and management to become messy when dealing with many form groups or even form arrays. For more on this aspect of reactive forms, please see Angular's documentation

  3. Create a submitContactSubmissionForm() method that we can initially use just to console log the value of the form

  4. Within the constructor, call this.submitContactSubmissionForm(). Using the inspector, we can technically see our newly created reactive form's value.

NOTE: Angular doesn't care what type our control is (booleans, string, and etc). They are treated the same.

Form Elements:

Right now we only have a working form, leading us to build the user interface for this form. In Angular, after the creation of the form control instance, we use a set of spatial directives to bind the instance to the HTML. Visually we can see this process is straightforward.

We add our angular implementation by using the Form Group directive to bind the Form Group to the contactSubmissionForm from the instance on the class. However, this alone, is not enough to bind the Form Group to the instance of the Form Group Control. We must register each one of the form elements on the group. Simply put, every property of the form group, will need to map to each one of the controls. This is accomplished by using the formControlName and register with the same name. If we look at our HTML we can notice that there wasn't much Angular needed within our HTML because we were able to handle it all within code.

Creating and understanding this form instance is basically the only learning curve that most developers starting with Angular will need to climb, which we see is manageable.


Are You Making The Connection?

The Bridge Between Angular and Loopback

NOTE: The following will only work if you have correctly followed the Angular 2 + Loopback Project Setup. Since we have built our Loopback SDK, we are now able to call these APIs within our Angular client so that you can save items with your reactive form. I will leave it up to you to update your Angular + Loopback project. To provide some assistance, please look at this code that I wrote when I was streaming about the topics that this blog covers. You will notice many difference in the way the forms are currently set up when compared to the form we recently created. If you are attempting to save your data and are getting errors, I would recommend making sure that you have your MySQL database setup correctly. Below we can see that now that our submitContactSubmissionForm() is calling a method that was created by Loopback. Calling this method allows us to save the results to the MySQL database that our Model is pointing to via the datasource.json file.

    
    import { Component, OnInit } from '@angular/core';
    import { Component, OnInit } from '@angular/core';
    import { LoopBackConfig } from '../../shared/sdk/index';
    import { FormBuilder, FormGroup } from '@angular/forms';
    import { ContactSubmissionApi } from '../../shared/sdk/services/custom/ContactSubmission';
    
    @Component({
      selector: 'app-contact-submission-form',
      templateUrl: './contact-submission-form.component.html',
      styleUrls: ['./contact-submission-form.component.css']
    })
    
    export class ContactSubmissionFormComponent implements OnInit {
      public contactSubmissionForm: FormGroup;
      submissionData;
      userAlert = {msg: ''};
      isSubmitted = false;
    
      constructor(private csAPI: ContactSubmissionApi, private fb: FormBuilder) {
        LoopBackConfig.setBaseURL('http://127.0.0.1:3000');
        LoopBackConfig.setApiVersion('api');
        this.createContactSubmissionForm();
      }
    
      ngOnInit() {
      }
    
      prepareContactSubmissionForm() {
        const formModel = this.contactSubmissionForm.value;
        const ContactSubmissionData = {
          firstName: formModel.firstName,
          lastName: formModel.lastName,
          email: formModel.userEmail,
          contactNumber: formModel.contactNumber,
          submission: formModel.submission
        };
        return ContactSubmissionData;
      }
    
      createContactSubmissionForm() {
        this.contactSubmissionForm = this.fb.group({
          firstName: '',
          lastName: '',
          userEmail: '',
          contactNumber: '',
          submission: ''
        })
      }
    
      submitContactSubmissionForm() {
        this.isSubmitted = true;
        this.submissionData = this.prepareContactSubmissionForm();
        this.csAPI.create(this.submissionData).subscribe();
        //TODO: Should actually do error handling
        this.userAlert.msg = "Looks like we saved it in the database!";
      }
      resetContactSubmissionForm() {
        this.contactSubmissionForm.reset();
      }
    
      closeAlert() {
        this.resetContactSubmissionForm();
        this.userAlert.msg = null;
        return true;
      }
    }
    

Next Blogs:

Creating Custom Remote Methods:

We will explore the use case of manipulating data between multiple models. Our client app will be able to fetch this data that wouldn't come from the APIs that Loopback creates for use automatically when we create new models

Services | Working with Data from a MySQL with Angular2:

We will improve our client app by using the custom remote methods that we previously create to make more dynamic views, such as a table listing all the items within our database.