Unleash Your Creativity: Crafting a Notepad-Style Rich Text Editor with Preview using Quill JS Library (Part 22) in Your Angular-15 Ionic-7 App

 Welcome back to our ongoing series on building a multiplatform application with Angular-15 and Ionic-7! In our previous articles, we explored a wide range of topics, including authentication, data management, task organization, calendar integration, event management, media management, UI customization, enhanced user authentication, real-time data manipulation, and creating a selectable and searchable data list. Today, in Part 22, we're diving into the creation of a notepad-style rich text editor and preview using the Quill JS library.

A rich text editor is an essential component in many applications, enabling users to format and stylize text as they would in a traditional word-processing program. By incorporating the Quill JS library, we can easily create a feature-rich and user-friendly rich text editor within our Angular-15 Ionic-7 app.


In this installment, we'll guide you through the process of creating a notepad-style rich text editor and preview using the Quill JS library. We'll start by integrating the Quill JS library into our app and configuring it to meet our requirements.


Next, we'll explore the various features and functionalities offered by Quill JS, including text formatting, styling, inserting images, creating lists, and more. We'll demonstrate how to handle user input, capture and manipulate the content, and provide a live preview of the formatted text.

Throughout this tutorial, we'll emphasize best practices for rich text editing, content manipulation, and user experience. By the end of this article, you'll have a solid understanding of how to create a notepad-style rich text editor and preview using the Quill JS library, enhancing the functionality and user engagement within your Angular-15 Ionic-7 app.

So, join us in Part 22 of our series as we embark on a journey into the realm of rich text editing. Together, let's harness the power of the Quill JS library to build a sophisticated and intuitive notepad-style editor within our application.

Tutorial

Here I have created a notepad-like feature where students can write their class notes using a rich text editor much like a Word document with all the formatting. I have used the Quill Library and API from this: ngx-quill - npm (npmjs.com) . I will install the library with: npm i ngx-quill command. Next, I will import the modules in main.ts file by creating a custom modules in the following way:

...
import { QuillModule, QuillModules } from 'ngx-quill';
//quill modules for the toolbar
//for customizing the toolbar, see <https://quilljs.com/docs/modules/toolbar/>
const modules: QuillModules = {
  toolbar: [
    ['bold', 'italic', 'underline', 'strike'], // toggled buttons
    ['blockquote', 'code-block'],
    [{ header: 1 }, { header: 2 }], // custom button values
    [{ list: 'ordered' }, { list: 'bullet' }],
    [{ header: [1, 2, 3, 4, 5, 6, false] }],
    [{ color: [] }, { background: [] }], // dropdown with defaults from theme
    [{ font: [] }],
    [{ align: [] }],
    ['clean'], // remove formatting button
    ['link', 'image', 'video'], // link and image, video
  ],
};

bootstrapApplication(AppComponent, {
  providers: [
    ...
      QuillModule.forRoot({
        modules,
        placeholder: 'Compose an epic...',
        theme: 'snow',
        format: 'object',
      }),
     ...
  ],
});

Also, I have imported the CSS file in global.scss like this:

//import quill snow for quill to work
@import "~quill/dist/quill.snow.css";

Now I will create a page where I will use the library to display the text editor with the command ionic generate page pages/notepad and write the logic in notepad.page.ts in the following way:

import { AfterViewInit, Component, OnInit, Output, ViewChild,} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule, ModalController, ToastController } from '@ionic/angular';
import { ContentChange, QuillEditorComponent, QuillModule } from 'ngx-quill';
import { NotepadModalPage } from '../notepad-modal/notepad-modal.page';

@Component({
  selector: 'app-notepad',
  templateUrl: './notepad.page.html',
  styleUrls: ['./notepad.page.scss'],
  standalone: true,
  imports: [IonicModule, CommonModule, FormsModule, QuillModule, NotepadModalPage,],})

export class NotepadPage implements OnInit {

  @ViewChild(QuillEditorComponent) editor!: QuillEditorComponent;
  // the above code is to get the editor component from the html file and store it in the editor variable of type QuillEditorComponent which is imported from the ngx-quill package

  myText = null;
  @Output() wordCount = 0; // this is the word count of the text in the editor and output is used to pass the data to the child component which is the notepad-modal page

  constructor(
    private modalCtrl: ModalController,
    private toastCtrl: ToastController
  ) {}

  ngOnInit() {}
  async preview() {
    console.log(this.myText);
    const modal = await this.modalCtrl.create({
      // this creates the modal and passes the data to the modal
      component: NotepadModalPage, // this is the modal page
      componentProps: {
        data: this.myText, // this is the data that is passed to the modal
        wordCount: this.wordCount, // this is the word count of the text in the editor
      },
    });
    await modal.present();
  }
  async clearEditor() {
    this.editor.quillEditor.setText(''); // this clears the editor content

    const toast = await this.toastCtrl.create({
      message: 'Note cleared',
      duration: 2000,
      position: 'bottom',
    });
    toast.present();
  }

  getWordCount() {
    // this function gets the word count of the text in the editor
    const text = this.editor.quillEditor.getText(); // this gets the text from the editor
    const words = text.match(/\\w+/g); // this matches the words in the text and stores it in the words variable as an array of words
    // the above code is a regular expression that matches the words in the text
    // \\w matches any word character (equal to [a-zA-Z0-9_])
    // + matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
    // g modifier: global. All matches (don't return after first match)
    // the above code is from <https://www.regular-expressions.info/wordboundaries.html>
    this.wordCount = words ? words.length : 0; // this gets the length of the words array and stores it in the wordCount variable if the words array is not null else it stores 0 in the wordCount variable
  }
}

The above component defines an editor variable of type QuillEditorComponent to reference the QuillEditorComponent in the HTML file, and a myText variable to store the text entered into the editor.

The component also defines an @Output() decorator with a wordCount variable that outputs the word count of the text entered into the editor. The getWordCount() function calculates the word count by using a regular expression to match words in the text.

The component has several functions including preview() which creates a modal that displays the text in the editor along with the word count, clearEditor() which clears the editor content and displays a toast message.

Next, I will use the above logic to display the text editor in the template file of notepad.page.html in the following way:

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>Notepad</ion-title>
    <ion-buttons slot="start">
      <ion-back-button defaultHref="/"></ion-back-button>
    </ion-buttons>
    <ion-buttons slot="end">
      <ion-button (click)="clearEditor()">
        <ion-icon
          slot="icon-only"
          color="danger"
          name="trash-outline"
        ></ion-icon>
      </ion-button>
    </ion-buttons>
    <ion-buttons slot="end">
      <ion-menu-button menu="main-menu"></ion-menu-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">Notepad</ion-title>
    </ion-toolbar>
  </ion-header>

  <quill-editor
    (onContentChanged)="getWordCount()"
    [(ngModel)]="myText"
  ></quill-editor>
  <ion-item lines="none"> Word count: {{ wordCount }}</ion-item>

  <ion-fab vertical="bottom" horizontal="end" slot="fixed">
    <ion-fab-button (click)="preview()">
      <ion-icon name="eye"></ion-icon>
    </ion-fab-button>
  </ion-fab>
</ion-content>

The ion-header component creates a toolbar at the top of the page that includes the title "Notepad" in an ion-title element, a back button in an ion-buttons element with a slot of "start", and a menu button in another ion-buttons element with a slot of "end". There is also a button with a trash can icon in an ion-buttons element with a slot of "end" that calls the clearEditor() method when clicked.

The quill-editor component is used to create a text editor that allows the user to enter and edit text. It is bound to the myText property using [(ngModel)]="myText", which allows two-way binding so that changes made in the editor are reflected in the myText property and vice versa. The (onContentChanged)="getWordCount()" event listener calls the getWordCount() method whenever the content of the editor changes, which updates the wordCount property with the number of words in the text.

Below the editor, there is an ion-item component that displays the word count using interpolation: Word count: {{ wordCount }}. Finally, there is an ion-fab (floating action button) component that displays a button with an eye icon. Clicking on the button calls the preview() method.

Next, I will create a modal component that will display the preview of the formatted text in notepad-modal.page.ts in the following way:

import { QuillModule } from 'ngx-quill';
import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule, ModalController, ToastController } from '@ionic/angular';
import { NotepadPage } from '../notepad/notepad.page';

@Component({
  selector: 'app-notepad-modal',
  templateUrl: './notepad-modal.page.html',
  styleUrls: ['./notepad-modal.page.scss'],
  standalone: true,
  imports: [IonicModule, CommonModule, FormsModule, QuillModule, NotepadPage],
})
export class NotepadModalPage implements OnInit {
  @Input() data: any;
  @Input() wordCount: any;

  constructor(
    private modalCtrl: ModalController,
    private toastCtrl: ToastController
  ) {}

  ngOnInit() {}

  async closeModal() {
    await this.modalCtrl.dismiss();
  }

  async saveNote() {
    // this function saves the note in the database
    console.log(this.data);
    this.closeModal();

    // this is where you can save the note in the database
    // present a toast message to the user that the note is saved

    const toast = await this.toastCtrl.create({
      message: 'Note saved',
      duration: 2000,
      position: 'bottom',
    });
    toast.present();
  }
}

The NotepadModalPage component has two input properties data and wordCount. The constructor function takes ModalController and ToastController as dependencies, which are used to control the modal and display toast messages respectively.

The component has two additional functions:

  • closeModal(): This function dismisses the modal when called.
  • saveNote(): This function logs the data of the note to the console, closes the modal, and displays a toast message confirming that the note has been saved.

And finally I will use the above logic to display the template in notepad-modal.page.html in the following way:

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>Note Preview</ion-title>
    <ion-buttons slot="start">
      <ion-button (click)="closeModal()"> Close </ion-button>
    </ion-buttons>
    <ion-buttons slot="end">
      <ion-button (click)="saveNote()"> Save </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">Note Preview</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-item> Word count: {{ wordCount }} </ion-item>
  <quill-view [content]="data"></quill-view>
  <ion-fab vertical="bottom" horizontal="end" slot="fixed">
    <ion-fab-button (click)="saveNote()">
      <ion-icon name="save"></ion-icon>
    </ion-fab-button>
  </ion-fab>
</ion-content>

The page's content is displayed within an ion-content component that takes up the full screen. There is an ion-item that displays the word count of the note using string interpolation.

The note content is displayed using the quill-view component, which is part of the ngx-quill module. This component is bound to the data input property, which should be a string that represents the note content. The quill-view component is used to render the note content as rich text, using the Quill.js library.

Finally, there is an ion-fab component at the bottom of the page, with a "Save" icon in it. This is a floating action button that allows the user to save the note. When the button is clicked, the saveNote() method is called, which should handle saving the note to the database and displaying a toast message to the user. This save functionality is not completely built for the moment but it can be easily integrated with a cloud database like Firebase. The above feature is demonstrated in figures 87, 88 and 89 below:

Figure 87: Text Editor Blank


Figure 88: Text Editor Full


Figure 89: Note Preview


Conclusion

In this twenty-second installment of our series on building a multiplatform application with Angular-15 and Ionic-7, we explored the creation of a notepad-style rich text editor and preview using the Quill JS library. By incorporating Quill JS into our Angular-15 Ionic-7 app, we enhanced the functionality and user experience by providing a feature-rich and intuitive rich text editing capability.

Rich text editors are a crucial component in many applications, allowing users to format and stylize text according to their preferences. With the Quill JS library, we seamlessly integrated a notepad-style editor that offers a wide range of features, including text formatting, styling, image insertion, and more.

Throughout this tutorial, we guided you through the process of integrating the Quill JS library into your app and configuring it to meet your requirements. We explored the various functionalities provided by Quill JS and demonstrated how to handle user input, manipulate content, and provide a live preview of the formatted text.

By following best practices for rich text editing, content manipulation, and user experience, you now possess the knowledge to create a sophisticated and user-friendly notepad-style rich text editor and preview within your Angular-15 Ionic-7 app.

We hope this tutorial has provided you with valuable insights and practical techniques for building a notepad-style rich text editor using the Quill JS library. By empowering your users with robust editing capabilities, you can enhance the versatility and interactivity of your application.

Thank you for joining us on this journey as we explored the fascinating world of app development with Angular-15, Ionic-7, and the Quill JS library. Stay tuned for future installments where we will continue to explore new features and functionalities to make our app even more powerful, dynamic, and user-friendly.

Happy coding!

Popular Posts

Perform CRUD (Create, Read, Update, Delete) Operations using PHP, MySQL and MAMP : Part 4. My First Angular-15 Ionic-7 App

Visualize Your Data: Showcasing Interactive Charts for Numerical Data using Charts JS Library (Part 23) in Your Angular-15 Ionic-7 App

How to Build a Unit Converter for Your Baking Needs with HTML, CSS & Vanilla JavaScript