Plans & PricingContact Us

How to create Angular Reactive Forms with a Rich Text Editor

October 10th, 2024

6 min read

The Angular log being investigated with a magnifying glass

Written by

Angular is a framework that aims to cater to a developer’s every need, with the overall goal of becoming the go-to framework for large, single-page applications. To reach that goal, it incorporates many features that simplify the development process, including Angular reactive forms.

This article looks at how to integrate rich text editing into an Angular reactive form using TinyMCE. It follows the same demo that our expert developer relations manager Mrina Sugosh shared in her ng-conf webinar, so feel free to watch the video and follow along. We’ve also provided all the code necessary in this guide to create a fully functional Angular WYSIWYG editor with TinyMCE’s world-class rich text editing capabilities.

A diagram showing the six steps to making an Angular reactive form with a rich text editor

Angular reactive form prerequisites and setup

To complete this guide and create an Angular reactive form with TinyMCE, you’ll need:

  • A demo Angular project: If you don’t already have a project for TinyMCE Angular integration, you can create a new one quickly for the purpose of this guide with the following shell command: 
ng new --defaults --skip-git tinymce-angular-demo

Step one: Add TinyMCE to Angular project

The first thing you’ll do is navigate to your project directory in Terminal. For this guide, we’re navigating into the tinymce-angular-demo folder. 

Install the TinyMCE Angular wrapper

After that, it’s time to install the TinyMCE Angular wrapper. This is just two shell commands.

cd tinymce-angular-demo
npm install @tinymce/tinymce-angular

The tinymce-angular component is a wrapper around the TinyMCE rich text editor. By default, the component will load TinyMCE from Tiny Cloud, which is the simplest and quickest way to get going.

H3: Import EditorModule to app.component.ts

Now that our WYSIWYG editor is installed, we want to add it to our Angular app’s app.component.ts file. It’s as easy as importing EditorModule from the tinymce-angular package into our app Component. Your app.component.ts file will look like this after you’ve imported the EditorModule

import { Component } from "@angular/core";
import { EditorModule } from "@tinymce/tinymce-angular";
import { RouterOutlet } from "@angular/router";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [RouterOutlet, EditorModule],
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  title = "forms-angular-rte";
}

Step two: Customize app.component.html file

Now that the rich text editor is available inside the Angular project, we’ll customize the app.component.html file to include it. It’s quick and easy to do so; you can use the following code for a basic editor. Just remember to replace “no-api-key” with a valid TinyMCE API key.

Replace everything in app.component.html with the following:

<main class="main">
  <editor 
    apiKey="no-api-key"
    [init]="{ 
      plugins: 'lists link image table code help wordcount',
      menubar: false,
      min_height: 150
     }"
  ></editor>
</main>

<router-outlet />

The foundation is set up, so now it’s time to create a reactive form and add our rich text editor. 

Step three: Add Material components

Now we’ll add Angular Material components to our project via a shell command.

ng add @angular/material

When you install Material, it will ask you three questions. For quick reference, you can answer: 

  1. Choose a prebuilt theme name: Choose “Custom” so that none of the default Material styles conflict with our demo.
  2. Set up global Angular Material typography styles: No.
  3. Include the Angular animations module: Include and enable.

Import MatCardModule to app.component.ts

For this step, all you need to do is add MatCardModule to app.component.ts.

import { Component } from "@angular/core";
import { EditorModule } from "@tinymce/tinymce-angular";
import { RouterOutlet } from "@angular/router";
import { MatCardModule } from "@angular/material/card";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [RouterOutlet, EditorModule, MatCardModule],
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  title = "forms-angular-rte";
}

Configure Material in app.component.html

Now that we have the MatCardModule library available to us, let’s wrap our text editor in a Material card in app.component.html

Step four: Add a Submit button to the reactive form

We have a card and text editor, so let’s add a Submit button now. 

Import MatButtonModule to app.component.ts

First we’ll bring in the MatButtonModule so we can make a button. 

import { Component } from "@angular/core";
import { EditorModule } from "@tinymce/tinymce-angular";
import { RouterOutlet } from "@angular/router";
import { MatCardModule } from "@angular/material/card";
import { MatButtonModule } from "@angular/material/button";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [RouterOutlet, EditorModule, MatCardModule, MatButtonModule],
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  title = "forms-angular-rte";
}

Add a button to app.component.html

Now that our button library is available to us, we can add it to our form. After we do, app.component.html looks like this: 

<main class="main">
  <mat-card class="blog mat-elevation-z3">
    <mat-card-title>TinyMCE & NG make a reactive form</mat-card-title>
    <mat-card-content>
    <editor 
      apiKey="no-api-key"
      [init]="{ 
        plugins: 'lists link image table code help wordcount',
        menubar: false,
        min_height: 150,
      }"
    ></editor>
  </mat-card-content>
  <mat-card-actions align="end">
    <button mat-raised-button>Submit</button>
  </mat-card-actions>
  </mat-card>
</main>

<router-outlet />

Now we’ve successfully created a form and a submit button!

Step five: Make the form reactive

We have a form and a submit button, but neither does anything yet. Let’s make our Angular reactive form actually reactive. 

Import ReactiveFormsModule and FormControl

As with all the other imports into app.component.ts, it’s easy to bring in FormControl and ReactiveFormsModule. These are the two necessary libraries we’ll need to complete our reactive form. 

Then the Component export must be altered to use FormControl and update the title of our form. Now your app.component.ts file should be:

import { Component } from "@angular/core";
import { EditorModule } from "@tinymce/tinymce-angular";
import { RouterOutlet } from "@angular/router";
import { MatCardModule } from "@angular/material/card";
import { MatButtonModule } from "@angular/material/button";
import { FormControl, ReactiveFormsModule } from "@angular/forms";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [
    RouterOutlet,
    EditorModule,
    MatCardModule,
    MatButtonModule,
    ReactiveFormsModule,
  ],
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  title = new FormControl("");
  updateTitle() {
    this.title.setValue("TinyMCE is awesome!");
  }
}

Add a reactive div to app.component.html

In app.component.html, between <mat-card-title> and <mat-card-content> you will:

  1. Create a <div> that will hold a reactive Title field
  2. Create an input field inside the <div> that relates to the formControl actions
.....
<mat-card class="blog mat-elevation-z3">
      <mat-card-title>TinyMCE & NG make a reactive form</mat-card-title>
      <div id="title_container">
        <label for="title">Title: </label>
        <input id="title" type="text" formControlName="title">
      </div>
      <mat-card-content>
....

Add the click action to the Submit button

To make sure that our Submit button corresponds to the code we wrote in app.component.ts, we need to bind it to updateTitle().

<mat-card-actions align="end">
    <button mat-raised-button (click)="updateTitle()">Submit</button>
</mat-card-actions>

Now we know that the form can be reactive. Time to add TinyMCE to the form in step six. 

Step six: Configure FormGroup

The next step is to change our form so that the TinyMCE editor’s input is part of the reactive form. To do this, we need to: 

  • Configure app.component.ts to create a new FormGroup with TinyMCE
  • Configure app.component.html to connect the FormGroup actions to the form

Create a new FormGroup in app.component.ts

To create a new FormGroup and add our RTE to it, we’ll import FormGroup from @angular/forms and then alter AppComponent so that TinyMCE fits inside.

We also created an onSubmit() function that will record the form’s values for the title and body to the browser console when the Submit button is pressed. We’ll connect all of this in the app.component.html file next.

import { Component } from "@angular/core";
import { EditorModule } from "@tinymce/tinymce-angular";
import { RouterOutlet } from "@angular/router";
import { MatCardModule } from "@angular/material/card";
import { MatButtonModule } from "@angular/material/button";
import { FormGroup, FormControl, ReactiveFormsModule } from "@angular/forms";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [
    RouterOutlet,
    EditorModule,
    MatCardModule,
    MatButtonModule,
    ReactiveFormsModule,
  ],
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  reactiveTinyForm = new FormGroup({
    title: new FormControl(""),
    body: new FormControl(""),
  });
  onSubmit() {
    console.warn(this.reactiveTinyForm.value);
  }
}

Alter app.component.html to accommodate the new form

First we need to create a FormGroup so that the title and body entered into our form will be logged in the console. That’s also where we point the ngSubmit action to our onSubmit() function.

[formControl]="title" becomes formControlName="title" in the input tag, and type="submit" is added to the Submit button at the bottom of the form. This is what it looks like: 

<main class="main">
  <form [formGroup]="reactiveTinyForm" (ngSubmit)="onSubmit()">
    <mat-card class="blog mat-elevation-z3">
      <mat-card-title>TinyMCE & NG make a reactive form</mat-card-title>
      <div id="title_container">
        <label for="title">Title: </label>
        <input id="title" type="text" formControlName="title">
      </div>
      <mat-card-content>
        <editor 
          apiKey="no-api-key"
          [init]="{ 
            plugins: 'lists link image table code help wordcount',
            menubar: false,
            min_height: 150,
            max_width: 650,
          }"
          formControlName="body"
        ></editor>
      </mat-card-content>
      <mat-card-actions align="end">
        <button mat-raised-button [disabled]="!reactiveTinyForm.valid">Submit</button>
      </mat-card-actions>
    </mat-card>
  </form>
</main>

<router-outlet></router-outlet>

Now we have a form that logs input into the console.

Final step: Validating reactive form input

Ideally, a user should not be able to submit an empty form. Right now, our reactive for will log an empty form into the console. We’ll add validation into the last step, which will fix that.

It’s easy to implement validation in an Angular reactive form in both app.component.ts and app.component.html. All you need to do is:

  • Import the Validators from @angular/forms to use in the title and body fields
  • Disable the Submit button in the form until there is content inside it

app.component.ts with added Validators

import { Component } from "@angular/core";
import { EditorModule } from "@tinymce/tinymce-angular";
import { RouterOutlet } from "@angular/router";
import { MatCardModule } from "@angular/material/card";
import { MatButtonModule } from "@angular/material/button";
import {
  Validators,
  FormGroup,
  FormControl,
  ReactiveFormsModule,
} from "@angular/forms";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [
    RouterOutlet,
    EditorModule,
    MatCardModule,
    MatButtonModule,
    ReactiveFormsModule,
  ],
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  reactiveTinyForm = new FormGroup({
    title: new FormControl("", [Validators.required]),
    body: new FormControl("", [Validators.required]),
  });
  onSubmit() {
    console.warn(this.reactiveTinyForm.value);
  }
}

Configure the Submit button in app.component.html

You won’t need to change any other code in app.component.html besides the Submit button under mat-card-actions.

  <mat-card-actions align="end">
    <button mat-raised-button [disabled]="!reactiveTinyForm.valid">Submit</button>
  </mat-card-actions>

Now the Submit button will be disabled until both title and the body fields have been filled out. 

Nice job! We have a beautiful Angular reactive form with powerful editing capabilities and rich text editing from TinyMCE.

An Angular Reactive form with TinyMCE

What’s next with your Angular reactive form and TinyMCE?

You can now use your Angular reactive form with the world’s leading RTE together with a sleek Material look and custom input validation, anywhere you’d like. Customize your app.component.css file for any look you want. From here, the possibilities are endless. 

If you haven’t already, check out the full range of TinyMCE rich text editor plugins and how they can help you provide the most productive editing experience for your users.

What are you doing with TinyMCE in Angular? Connect with TinyMCE on X (formerly Twitter) and let us know what you’re up to.

Coco Poley is the Technical Content Marketer for TinyMCE - the leading WYSIWYG rich text editor powering 40% of the internet. Coco has over eight years of professional experience in technical content creation, educational material production, content writing, data engineering, and software quality assurance engineering.

Related Articles

Join 100,000+ developers who get regular tips & updates from the Tiny team.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.