LF starter guide

Welcome to Lightweightform (LF) - an angular based open source web framework that simplifies the development of complex web forms. It provides developers a set of libraries and resources that alleviate the burden associated with complex form programming.

This guide will help you getting started with our framework and after you complete the guide you will be able to start to develop amazing complex forms easily.

Requirements

Setup

Guide

In this starter example you will develop an LF based Self Check-in complex form.

Self Check-in is a hypothetical web-form where the Hotel/Guest House owner will gather information about its guests. Despite being a simple form, it is compliant with the local Hotel policies and demonstrates some LF features, like repeatable forms and validations. Have fun.

This guide is divided in six checkpoints:

Checkpoint #1

Goal: Define the structure of the application

  1. Start the application executing the command ng serve cp1-begin
  2. Define the application's title ("LF Self check-in app")
  3. Add the first form of the application called reservation-detail:

    3.1. Generate component with angular cli ng generate component components/reservation-detail

    3.2. Specify the component inside the app schema <sc-reservation-detail></sc-reservation-detail>

    3.3. Change the HTML of reservation-detail component. Add a lf-form with name "reservationDetail":

        <lf-form name="reservationDetail"
                 label="Reservation detail"
                 [isRequired]="true"> <!-- By default forms are optional -->
          <lf-form-content>
          </lf-form-content>
        </lf-form>
    
  4. Add a form-list, called "guests", to the application:

    4.1. Generate component with angular cli ng generate component components/guests

    4.2. Specify the component inside the app schema <sc-guests></sc-guests>

    4.3. Change the HTML of reservation-detail component. Add an lf-form-list with name "guests":

        <lf-form-list name="guests" label="Guests" [minSize]="1">
          <ng-template #lfFormTemplate let-i="index">
            <lf-form #singlePerson [name]="i" label="Guest">
              <lf-form-content>
              </lf-form-content>
            </lf-form>
          </ng-template>
        </lf-form-list>
    
  5. Add a sub-form inside reservation-detail's component:

    5.1. Generate a sub form called accommodation (path: "components/reservation-detail/components/accommodation", name: "accommodation", label: "Accommodation", isRequired: true)

    5.2. Put this component inside the reservation detail component as shown below:

        <lf-form name="reservationDetail"
             label="Reservation detail"
             [isRequired]="true">
            <lf-form-content></lf-form-content>
            <sc-accommodation></sc-accommodation>
        </lf-form>
    
  6. In the same way, add two new sub forms to guests with the specification indicated below:

    6.1. Food component: path: "components/guests/components/food", name: "food", label: "Food"

    6.2. Other Services component: path: "components/guests/components/other-services", name: "otherServices", label: "Other Services"

    6.3. Put the components inside the guests component as shown below:

        <lf-form-list name="guests" label="Guests" [minSize]="1">
          <ng-template #lfFormTemplate let-i="index">
            <lf-form #singlePerson [name]="i" label="Guest">
              <lf-form-content>
              </lf-form-content>
              <sc-food></s  c-food>
              <sc-other-services></sc-other-services>
            </lf-form>
          </ng-template>
        </lf-form-list>
    
  7. Indicate that the reservation-detail component is the default/initial component of the application

        <lf-form name="reservationDetail"
                 label="Reservation detail"
                 [isRequired]="true"
                 [isDefault]="true">
          <lf-form-content>
          </lf-form-content>
          <sc-accommodation></sc-accommodation>
        </lf-form>
    

Checkpoint #2

Goal: Fill the form components with LF fields

  1. Start the application executing the command ng serve cp2-begin
  2. Add the fields below to reservation-detail component:

    2.1. lf-text (name: "email", code: "1.1", label: "E-mail address", minLength: 1)

    2.2. lf-text (name: "phone", code: "1.2", label: "Phone Number", minLength: 1)

    2.3. lf-date-range (name: "checkincheckout", code: "1.3", label: "Check-in / Check-out", isRequired: true)

    2.4. lf-number (name: "arrivalHour", code: "1.4", label: "Hour of arrival")

    2.5. lf-text (name: "flightNumber", code:"1.5", label: "Flight Number")

  3. Add three tables for each meal on the food component using the sc-food-table previously created:

        <sc-food-table name="breakfast" label="Breakfast"></sc-food-table>
        <sc-food-table name="lunch" label="Lunch"></sc-food-table>
        <sc-food-table name="dinner" label="Dinner"></sc-food-table>
    

Checkpoint #3

Goal: Implement schema specifications for the LF fields

  1. Start the application executing the command ng serve cp3-begin
  2. Add a max date schema specification to the "birthDate" field on the guest component

        /* guest.component.ts */
        public get today {
            return this.appService.today();
        }
        <!-- guest.component.html -->
        <lf-date name="birthDate"
                 [maxDate]="today"
                 label="Date of Birth"
                 [isRequired]="true">
        </lf-date>
    
  3. Add an e-mail schema specification to the "email" field on the reservation-detail component

        /* reservation-detail.component.ts */
         public get emailValidator() {
           return ctx => {
             const email = ctx.get();
             if (!this.appService.isValidEmail(email)) {
               return {
                 code: 'INVALID_EMAIL',
                 message:
                   'Email is invalid',
               };
             }
           };
         }
        <!-- reservation-detail.component.html -->
        <lf-text name="email" ... [validator]="emailValidator"></lf-text>
    
  4. Add a max length schema specification to the "flightNumber" field in reservation-detail

        <lf-text name="flightNumber" code="1.5" title="Flight Number" label="Flight Number" [maxLength]="7"></lf-text>
    
  5. Add min and max schema specifications to the "arrivalHour" field on the reservation-detail component

        <lf-number name="arrivalHour" code="1.4" title="Hour of Arrival" label="Hour of Arrival" [min]="0" [max]="23"></lf-number>
    
  6. Add a schema specification to the "personalTrainer" field in other-services, so that the field is only required if the guest opts for gym service

        <lf-number name="personalTrainer"
                   label="Days With Personal Trainer"
                   [isRequired]="asksForGymService">
        </lf-number>
    

Checkpoint #4

Goal: Implement LF fields with computed values

  1. Start the application executing the command ng serve cp4-begin
  2. Notice the existing function "computedPrice" in the "foodTable" component
  3. Make the value of the field "price" of "foodTable" depend on the "typeFood" field value, using the "computedPrice" function

        <lf-number name="price"
                   [isDisabled]="true"
                   [computedValue]="computedPrice"
        ></lf-number>
    
  4. Notice the "subTotal" computed getter function of the "food-table" component that returns the sum total of the the "food-table"'s prices

  5. Create an Angular binding of "subTotal" in "food-table"

        <lf-table #lfTable
                  [name]="name"
                  [label]="label">
            ...
        </lf-table>
        {{subTotal}}
    
  6. Set the field "totalPrice" of "food" as computed, using the existing "totalPrice" function

Checkpoint #5

Goal: Customize values display

  1. Start the application executing the command ng serve cp5-begin

  2. Let's format the computed values to make our self check-in application look better

  3. Add the following fields to "totalPrice" in "food", and notice the changes on the value's formatting

        <lf-number name="totalPrice"
                   legend="Total Price"
                   [isDisabled]="true"
                   [representsInteger]="false"
                   [scale]="2"
                   decimalSeparator=","
                   thousandsSeparator="."
                   suffix=" €"
                   [computedValue]="totalPrice"
        ></lf-number>
    
  4. Add the same fields to "price" in "food-table"

  5. To customize the "subTotal" binding in "food-table" we will need to use the Angular pipe system. In this case, check the following example to customize the value for currency display

        {{subTotal | currency:'EUR'}}
    
  6. To align the "subTotal" value, we can encase it in an HTML tag and set its alignment

        <p class="pull-right"> {{subTotal | currency:'EUR'}} <p>
    

Checkpoint #6

Goal: Add form's actions to the LF application

  1. Start the application executing the command ng serve cp6-begin
  2. Understand how to add actions, such as load, save and submit to the self check-in application

        <!-- app.component.html -->
        <lf-app [actions]="actions">...</lf-app>
        /* app.component.ts */
        actions = [
            {
              text: 'Save',
              style: 'outline-secondary',
              icon: 'save',
              callback: () => {
                alert('save not implemented');
              },
            },
          ];
    
  3. Add a save action to save the form content to a file, using the function that is provided by the application core:

        {
          text: 'Save',
          style: 'outline-secondary',
          icon: 'save',
          callback: () => {
           this.lfApp.saveToFile('selfcheckin.json', {
            onSuccess: () => console.log('Value saved successfully'),
            onError: (err) => console.error('Error saving file: ', err),
           });
          },
        },
    
  4. In the same way, implement a load action:

        callback: () => {
          this.lfApp.loadFromFile({
            onSuccess: () => console.log('Value loaded successfully'),
            onError: (err) => console.error('Error loading file: ', err),
           });
        },
    
  5. Add a validate action that shows all form's errors

         callback: () => this.lfApp.validate(),
    
  6. Add a submit action that submits the form to the self check-in server https://selfcheckin.opensoft.pt (use the submit function already implemented).