Categories
Angular JavaScript

How to Add Rich Text Editor to an Angular App with Localization

Adding a rich text editor to your Angular app is easy since there are libraries to do so already. It supports lots of features like changing fonts, adding or removing underline, adding lists, bolding text, changing default fonts, add pictures, adding placeholders, etc. Almost anything you can think of can be added to an editor. CKEditor is the rich text editor with the most comprehensive options available.

To use CKEditor in our Angular app, we use an Angular version of the editor available at https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/frameworks/angular.html, available as a Node package. This allows us to bind our input content to our model without writing code to do that ourselves.

We install the package by running:

npm i ng2-ckeditor

Then we include the following in the imports array of your module:

import { CKEditorModule } from 'ng2-ckeditor';  
import { FormsModule } from '@angular/forms';  
   
@NgModule({  
  // ...  
  imports: [CKEditorModule, FormsModule],  
  // ...  
})  
export class AppModule {}

In index.html , we add:

<script src="https://cdn.ckeditor.com/4.5.11/full-all/ckeditor.js"></script>

This includes all the plugins for CKEditor.

Then we add the following to our component, we add:

import { Component } from '@angular/core';  
   
@Component({  
  selector: 'sample',  
  template: `  
  <ckeditor  
    [(ngModel)]="content"  
    [config]="config"  
    [readonly]="false"  
    (change)="onChange($event)"  
    (editorChange)="onEditorChange($event)"   
    (ready)="onReady($event)"  
    (focus)="onFocus($event)"  
    (blur)="onBlur($event)"  
    (contentDom)="onContentDom($event)"  
    (fileUploadRequest)="onFileUploadRequest($event)"  
    (fileUploadResponse)="onFileUploadResponse($event)"  
    (paste)="onPaste($event)"  
    (drop)="onDrop($event)"  
    debounce="500">  
  </ckeditor>  
  `,  
})  
export class Editor{  
  content: string = '<p>Some html</p>'; 
  config: any = {  
    allowedContent: true,  
    toolbar: [['Bold', 'Italic', 'Underline', '-', 'NumberedList', 'BulletedList', 'Link', '-', 'CreatePlaceholder']],  
    removePlugins: 'elementspath',  
    resize_enabled: false,  
    extraPlugins: 'font,divarea,placeholder',  
    contentsCss: ["body {font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;}"],  
    autoParagraph: false,  
    enterMode: 2  
  };  
}

In the config , we define the options that are displayed in the editor. We can define the buttons shown in the toolbar with the toolbar property. We put all the options in the array. Full list of controls that you can display in the toolbar is at https://ckeditor.com/latest/samples/old/toolbar/toolbar.html. We have one array per toolbar row. We can also remove plugins that we don’t need with the removePlugins option, and add more plugins with the extraPlugins options. Plugins have to be installed separately if the full installation is not used like I did above.

It can handle model changes with the change handler. You can run code when the editor is loaded with the ready event. paste handler allows you to run code after something is pasted.

Now to change the language of the editor, we can add the language option to the config object. Use the standard language codes listed at https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes. For example, to set the language to Spanish, we put the following in the config object:

language: 'es',

It is very simple.

Another thing you might want to do with your editor is to customize the content of some of the elements. To do this, we can make an Angular directive and add some event handlers to the CKEDITOR object to set the content you want. For example, if you want to set labels of the inputs of the Placeholder plugin that we have above, we run:

ng g directive changeEditorOptions

Then we get:

import { Directive } from '@angular/core';  
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective { 
  constructor() {}
}

Next, we reference the CKEDITOR global object we got from putting <script src=”https://cdn.ckeditor.com/4.5.11/full-all/ckeditor.js"></script> in index.html and hook some event handlers to the CKEDITOR object and put that into the constructor. After that we get the following:

import { Directive } from '@angular/core';  
  
declare let CKEDITOR: any;  
declare let $: any;
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 

  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
  
      }  
    }); 
}}

instanceReady event is fired when the editor is loaded, and dialogDefinition is loaded when the user opens a dialog box from the editor.

To add a drop down to our Placeholders dialog box and change the placeholders available in the placeholder plugin we can add it into the dialogDefinition event handler. We can make our changes by adding the following:

if ('placeholder' === event.data.name) {  
  const input = event.data.definition.getContents('info').get('name');  
  const dialog = event.data.definition;  
  input.type = 'select';  
  input.items = [  
    ['First Name', 'first_name'],  
    ['Last Name', 'last_name'],  
    ['Link', 'link'],  
  ];}

The code works like this: after opening the dialog, we get the name element in the info section by calling event.data.definition.getContents(‘info’).get(‘name’); Then we change the input to select by setting that to input.type and we populate the input by assigning the array to input.items . The first item in the array is what is displayed and the second is the value.

At the end, we have:

import { Directive } from '@angular/core';  
  
declare let CKEDITOR: any;  
declare let $: any;

@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 
  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
        const input = event.data.definition.getContents('info').get('name');  
        const dialog = event.data.definition;  
        input.type = 'select';  
        input.items = [  
          ['First Name', 'first_name'],  
          ['Last Name', 'last_name'],  
          ['Link', 'link'],  
        ];  
      }  
    }); }  
}

To run script with the editor loads, we put the following in the instanceReady handler:

const $script = document.createElement('script'),  
$editor_instance = CKEDITOR.instances[ev.editor.name];
$script.src = '//path/to/your/script';  
$script.onload = () => {  
  //run code after load  
}};

$editor_instance.document.getHead().$.appendChild($script);

Now we have:

import { Directive } from '@angular/core';  
  
declare const CKEDITOR: any;
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 
  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
         const $script = document.createElement('script'),  
         $editor_instance = CKEDITOR.instances[ev.editor.name]; 
         $script.src = '//path/to/your/script';  
         $script.onload = () => {  
           //run code after load  
         }}; $editor_instance.document.getHead().$.appendChild($script);  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
        const input = event.data.definition.getContents('info').get('name');  
        const dialog = event.data.definition;  
        input.type = 'select';  
        input.items = [  
          ['First Name', 'first_name'],  
          ['Last Name', 'last_name'],  
          ['Link', 'link'],  
        ];  
      }  
    }); }  
}

Since CKEDITOR is global, we need to put declare keyword before it so that the compiler won’t think it’s undefined.

Apply this directive to your editor by adding:

import { Component } from '@angular/core';  
   
@Component({  
  selector: 'sample',  
  template: `  
  <div appChangeEditorOptions>    
    <ckeditor  
      [(ngModel)]="content"  
      [config]="config"  
      [readonly]="false"  
      (change)="onChange($event)"  
      (editorChange)="onEditorChange($event)"   
      (ready)="onReady($event)"  
      (focus)="onFocus($event)"  
      (blur)="onBlur($event)"  
      (fileUploadRequest)="onFileUploadRequest($event)"  
      (fileUploadResponse)="onFileUploadResponse($event)"  
      (paste)="onPaste($event)"  
      (drop)="onDrop($event)"  
      debounce="500">  
    </ckeditor>  
  </div>  
  `,  
})  
export class Editor {  
  content: string = '<p>Some html</p>';
  config: any = {  
    allowedContent: true,  
    toolbar: [['Bold', 'Italic', 'Underline', '-', 'NumberedList', 'BulletedList', 'Link', '-', 'CreatePlaceholder']],  
    removePlugins: 'elementspath',  
    resize_enabled: false,  
    extraPlugins: 'font,divarea,placeholder',  
    contentsCss: ["body {font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;}"],  
    autoParagraph: false,  
    enterMode: 2  
  };  
}

Also include, ChangeEditorOptionsDirective in the app.module.ts into the declaration section of the module if it’s not there already.

Categories
Angular JavaScript

Defining and Using Angular Attribute Directives

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at how to create and use attribute directives with Angular.

Build a Simple Attribute Directive

We can create a simple directive by using the Angular CLI.

To create one, we run:

ng generate directive highlight

to create the skeleton code for our directive.

Once we did that, we can write some code for our directive. We write the following code:

highlight.directive.ts :

import { Directive, ElementRef } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  constructor(el: ElementRef) {  
    el.nativeElement.style.backgroundColor = 'pink';  
  }  
}

The code above takes an ElementRef of the element that the directive is applied to.

Then we set the backgroundColor of it to 'pink' .

The bracket makes our directive an attribute directive.

We should prefix our directive names with something like app so that they don’t clash with HTML attribute names.

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { HighlightDirective } from './highlight.directive';

@NgModule({  
  declarations: [  
    AppComponent,  
    HighlightDirective  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

We added HighlightDirective to our AppModule so we can use it.

Then we can use it in app.component.html as follows:

<div appHighlight>foo</div>

We then should see a pink div with ‘foo’ inside it.

Respond to User-Initiated Events

We can also use directives to detect and act on user-initialed events.

For example, we can write the change the HighlightDirective we had above as follows:

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  constructor(private el: ElementRef) {    } 

  @HostListener('mouseenter') onMouseEnter() {  
    this.highlight('pink');  
  } 

  @HostListener('mouseleave') onMouseLeave() {  
    this.highlight(null);  
  } 

  private highlight(color: string){  
    this.el.nativeElement.style.color = color;  
  }  
}

We added the HostListener decorators to listen to the mouseenter and mouseleave events of the element that our highlight directive is applied to.

Therefore, when we hover over our text, we’ll see it turn pink. Otherwise, it’ll go back to its normal color.

Pass Values Into the Directive with an @Input Data Binding

We can use the @Input decorator as we do with components to pass data to directives.

For example, we can write the following:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input() highlightColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor;  
  }  
}

app.component.html :

<div appHighlight highlightColor='pink'>foo</div>

In the code above, we added the highlightColor input and then pass in the color string in app.component.html to set the color.

We’ll then get pink text since we set highlightColor to pink.

Bind to an @Input Alias

We can make the code above shorter if we change the @Input alias to have the same name as the directive name.

For example, we can write:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input('appHighlight') highlightColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor;  
  }  
}

Then we can use it as follows in app.component.html ;

<div appHighlight='pink'>foo</div>

As we can see, we don’t have to specify highlightColor separately anymore.

Bind to a Second Property

We can have multiple @Input s to bind to multiple properties.

For example, we can write the following:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input('appHighlight') highlightColor: string;  
  @Input() defaultColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor || this.defaultColor;  
  }  
}

In the code above, we have 2 @Inputs instead of one.

Now we can use it as follows in app.component.html :

<div appHighlight='pink' defaultColor='yellow'>foo</div>  
<div appHighlight defaultColor='green'>bar</div>

In the code above, we have appHighlight directive applied without any color string set in the second div.

Therefore, since defaultColor is set to 'green' , it’ll be applied as we specified in highlight.directive.ts ‘s ngOnInit method.

Conclusion

We define attribute directives by running ng g directive and name them with brackets outside the name.

Then we can pass in properties with @Input s and listen to events with @HostListener .

Also, we get the element that the directive is applied to and then apply whatever styles we want to it.

Categories
Angular JavaScript

Angular Reactive Forms — Patching Values and Building Forms

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at how to update values in the form group and use FormBuilder .

Patching the Model Value

There’re 2 ways to update the model value of Reactive forms.

We can either call setValue on an individual control to update the value or call patchValue on a FormGroup to replace values in the form model.

We can call setValue as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl, FormGroup } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  personForm = new FormGroup({  
    name: new FormControl(""),  
    address: new FormGroup({  
      street: new FormControl("")  
    })  
  }); 

  updateName() {  
    this.personForm.setValue({ ...this.personForm.value, name: "Jane" });  
  }  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="name" placeholder="Name" />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" />  
  </div>  
</form><button (click)="updateName()">Update Name</button>

setValue strictly adheres to the structure of the form group, so we have to include everything as we did in the updateName method.

If we only want to update some values, we can use the patchValue method as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl, FormGroup } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  personForm = new FormGroup({  
    name: new FormControl(""),  
    address: new FormGroup({  
      street: new FormControl("")  
    })  
  }); 

  updateName() {  
    this.personForm.patchValue({ name: "Jane" });  
  }  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="name" placeholder="Name" />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" />  
  </div>  
</form><button (click)="updateName()">Update Name</button>

In the code above, the updateName method changed to:

this.personForm.patchValue({ name: "Jane" });

As we can see, we don’t have to include all the fields. We only included the ones we want to update.

Generating Form Controls with FormBuilder

We can use the FormBuilder class to make creating Reactive forms easier for us.

To create a form with FormBuilder , we can write the following:

app.component.ts :

import { Component } from "@angular/core";  
import { FormBuilder } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  constructor(private fb: FormBuilder) {} 
  personForm = this.fb.group({  
    name: [""],  
    address: this.fb.group({  
      street: [""]  
    })  
  });  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="name" placeholder="Name" />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" />  
  </div>  
</form>

The code above has the same functionality as the other examples.

We have a personForm with the name field in the root form group and the street field nested in the address form group.

Simple Form Validation

To make sure what’s inputted if complete and correct, we have to add form validation to our form.

With Reactive forms, we can ads a single validator to our form control and also display the overall form status.

We can add validation as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormBuilder, Validators } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  constructor(private fb: FormBuilder) {} 
  personForm = this.fb.group({  
    name: ["", Validators.required],  
    address: this.fb.group({  
      street: ["", Validators.required]  
    })  
  });  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="name" placeholder="Name" required />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" required />  
  </div>  
</form>

In the code above, we made the name and street fields required by importing the Validator and using the required property in each field.

Also, we have to add the required attribute in the inputs to prevent errors when the expression has changed after the template has been checked.

Displaying Form Status

We can reference the status property of our form to get the validator status of our form.

To do this, we just change app.component.html to the following:

<form [formGroup]="personForm">  
  <p>Form Status: {{ personForm.status }}</p>  
  <input type="text" formControlName="name" placeholder="Name" required />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" required />  
  </div>  
</form>

Now the form should show the validation status of it. personForm.status returns 'INVALID' if it’s not valid and 'VALID' if it’s valid.

Therefore, we should see that in the first line of the form.

Dynamic Controls Using Form Arrays

To add dynamic controls, we can use the FormArray class.

We can use FormArray as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormBuilder, FormArray } from "@angular/forms";

@Componen({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  constructor(private fb: FormBuilder) {}  
  personForm = this.fb.group({  
    names: this.fb.array([this.fb.control("")])  
  }); 

  get names() {  
    return this.personForm.get("names") as FormArray;  
  } 

  addName() {  
    this.names.push(this.fb.control(""));  
  }  
}

app.component.html :

<form [formGroup]="personForm">  
  <div formArrayName="names">  
    <h3>Names</h3>  
    <button (click)="addName()">Add Name</button>  
    <div *ngFor="let n of names.controls; let i=index">  
      <label>  
        Name:  
        <input type="text" [formControlName]="i" />  
      </label>  
    </div>  
  </div>  
</form>

In the code above, we created the names form array in personForm .

Then in the template, we loop through the controls with the *ngFor in the form.

The formControlName is set to the index of the form control.

We also added an Add Name button to add a new form control.

Conclusion

We can use the FormBuilder class to save us time when we want to build forms.

It’s also useful for building dynamic forms since it takes FormArrays .

We can get the status of a form with the status property of the form group.

To update the values of the form, we can call the setValue or patchValue method of a form group.

Categories
Angular JavaScript

Introduction to Angular Reactive Forms

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at the basics of creating reactive forms with Angular.

Reactive Forms

Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change returns a new state, which maintains the integrity of the model between changes.

It’s also easier to test because the data is consistent and predictable.

We can create a Reactive form as follows:

app.module.ts :

import { BrowserModule } from "@angular/platform-browser";  
import { NgModule } from "@angular/core";  
import { ReactiveFormsModule } from "@angular/forms";  
import { AppComponent } from "./app.component";

@NgModule({  
  declarations: [AppComponent],  
  imports: [BrowserModule, ReactiveFormsModule],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule {}

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  nameControl = new FormControl("");  
}

app.component.html :

<input type="text" [formControl]="nameControl" />  
<p>{{ nameControl.value }}</p>

In the code above, we imported the ReactiveFormsModule in app.module.ts .

Then in AppComponent , we added a FormControl called nameControl .

Then we connect our input element to nameControl by using the formControl property.

To display the value we typed in, we reference the nameControl ‘s value property as we did with the p element.

Replacing a Form Control Value

We can call the setValue method on a FormControl to replace the value of it.

For example, we can write the following code:

app.component.ts

import { Component } from "@angular/core";  
import { FormControl } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  nameControl = new FormControl(""); 

  updateName() {  
    this.nameControl.setValue("Jane");  
  }  
}

app.component.html :

<input type="text" [formControl]="nameControl" />  
<button (click)="updateName()">Update Name</button>

In the code above, we have the updateName method which calls setValue to set the value of FormControl .

When we click the button, we’ll see Jane entered in the input.

Grouping Form Controls

We can create a FormGroup to group form controls together.

For example, we can use it as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl, FormGroup } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  personForm = new FormGroup({  
    nameControl: new FormControl("")  
  });  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="nameControl" />  
</form>

We bound the form to personForm by using the formGroup property.

Then we set the form control in the form group by using the formControlName property.

Submitting Form Data

We can submit data entered into a Reactive form by attaching an onSubmit handler to our form.

To do that we can write the following:

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl, FormGroup } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  personForm = new FormGroup({  
    nameControl: new FormControl("")  
  }); onSubmit() {  
    console.log(this.personForm.value);  
  }  
}

app.component.html :

<form [formGroup]="personForm" (ngSubmit)="onSubmit()">  
  <input type="text" formControlName="nameControl" />  
  <button type="submit">Submit</button>  
</form>

In the code above, we have the onSubmit handler, which gets the value of all the fields entered by accessing the value property of the form group.

When we click Submit, we should see what we typed in.

To disable the Submit button if the form isn’t valid, we can write:

app.component.html:

<button type="submit" [disabled]="!personForm.valid">Submit</button>

to set the disabled property of the button when the personForm ‘s valid value.

Creating Nested Form Groups

We can nest form groups. For example, we can write the following code to do that:

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl, FormGroup } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  personForm = new FormGroup({  
    name: new FormControl(""),  
    address: new FormGroup({  
      street: new FormControl("")  
    })  
  }); onSubmit() {  
    console.log(this.personForm.value);  
  }  
}

app.component.html :

<form [formGroup]="personForm" (ngSubmit)="onSubmit()">  
  <input type="text" formControlName="name" placeholder="Name" />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" />  
  </div>  
  <button type="submit">Submit</button>  
</form>

In the code above, we have the personForm form group with the name FormControl in the root.

Then we added the address FormGroup , which has the street FormControl inside.

In app.component.html , we bind the form to the personForm FormGroup as usual, but we have an extra div to bind the div and what’s inside to the address FormGroup .

Then we bind the street FormControl to our input inside.

Conclusion

We can create FormControl s and bind input elements to them.

Also, we can create FormGroup s and bind forms to them.

FormGroup s can be nested within each other.

We can access a FormGroup and FormControl ‘s values with the value property.

Categories
Angular JavaScript

Introduction to Angular Forms

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at the basics of creating forms with Angular.

Different Kinds of Forms

Angular provides 2 different approaches to handling user input through forms.

They are either reactive or template-driven. They both capture events from the view, validate their user input, and create a form model and data model to update, and provide a way to track changes.

Reactive forms are more robust. They’re more scalable, reusable and testable.

Template-driven forms are useful for adding a simple form to an app. They’re easy to add, but they don’t scale as well as reactive forms.

Common Foundation

Both kinds of forms share the following building blocks:

  • FormControl — tracks the value and validation status of individual form controls
  • FormGroup — tracks the same value and status for a collection of form controls
  • FormArray — tracks the same value and status for an array of form control.s.
  • ControlValueAccessor — creates a bridge between Angular FormControl instances and native DOM elements.

Form Model Setup

We can set up the form model of a Reactive form by setting the formControl property of an input element.

For example, we can write the following code:

app.module.ts :

import { BrowserModule } from "@angular/platform-browser";  
import { NgModule } from "@angular/core";  
import { ReactiveFormsModule } from "@angular/forms";  
import { AppComponent } from "./app.component";

@NgModule({  
  declarations: [AppComponent],  
  imports: [BrowserModule, ReactiveFormsModule],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule {}

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  nameControl = new FormControl("");  
}

app.component.html :

<input type="text" [formControl]="nameControl" />

In the code above, we added the ReactiveFormsModule to our AppModule .

Then we created nameControl in AppComponent .

Finally, we set the formControl property to nameControl to connect the input to the FomControl .

Setup in Template-Driven Forms

We can use the ngModel directive to bind our input value to the a field in our component.

For example, we can write the following:

app.module.ts :

import { BrowserModule } from "@angular/platform-browser";  
import { NgModule } from "@angular/core";  
import { FormsModule } from "@angular/forms";  
import { AppComponent } from "./app.component";

@NgModule({  
  declarations: [AppComponent],  
  imports: [BrowserModule, FormsModule],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule {}

app.component.ts :

import { Component } from "@angular/core";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  name: string;  
}

app.component.html :

<input type="text" [(ngModel)]="name" />

In the code above, we imported the FormsModule in app.module.ts .

Then we created the name field in AppComponent to bind the data to the field.

Then we bind our input’s value to name via ngModel .

Both Reactive and Template-Driven forms provide 2 way binding between the model and the input.

Data Flow in Reactive Forms

From view to model, the data flow follows the following steps:

  1. User types something into the input
  2. Form input emits an input event
  3. Control value accessor listens for events on the form input and sends the new value to the FormControl instance.
  4. The FormControl instance emits the new value through the valueChanges observable.
  5. Subscribes to valueChanges receive the new value

From model to view, the flow is as follows:

  1. The user calls the nameControl.setValue method to update the FormControl value
  2. The FormControl instance emits the new value through the valueChanges observable.
  3. Any subscribers to the valueChanges observable receive the new value
  4. The control value accessor on the form input element updates the element with the new value

Data Flow in Template-Driven Forms

The data from the view to the model in template-driven forms are as follows:

  1. User types something into the input element.
  2. The input element emits an input event with the value typed in
  3. The control value accessor attached to the input triggers the setValue method on the FormControl instance
  4. The FormControl instance emits the new value through valueChanges observable
  5. Any subscribers to the valueChanges observable receives the new value
  6. The control value accessor also calls NgModel.viewToModelUpdate method which emits an ngModelChange event
  7. ngModelChanges triggers model value update

From model to view, the flow is as follows:

  1. The value is updated in the component
  2. Change detection begins
  3. ngOnChanges is called on the NgModel directive instance because the input value has changed
  4. ngOnChanges queues an async task to set the value of the FormControl instance
  5. Change detection completes
  6. The task to set FormControl instance value is run on next tick
  7. FormControl instance emits the latest change through the valueChanges observable
  8. Any subscribers to the valueChanges observable receives the new value
  9. Control value accessor updates the form input element in the view

Conclusion

We can create Angular forms as Reactive or Template-Driven forms.

Reactive forms are more robust and Template-Driven forms are suitable for simple forms.

They both have the same parts. The control value accessor takes the input value and sets it to the model.