Tech Incent
Angular

Angular code (JSON, YAML, TypeScript) editor example

In this article, I will build a generic code editor component. I will show the JSON code editor example but it can be changed with the available type of code mode (YAML, Javascript, TypeScript) editor.

For this code editor example, I will use “ace code editor” and it depends on ace-builds package

Note: Ace-builds are just generated files with typescript support. The main editor is ACE

Generate a new fresh angular project

ng new angular-code-editor

Install dependency package

npm i ace-builds

Generate editor module and editor components

ng g m editor
ng g c editor --export

I will generate a specific module for our custom editor. When any feature can module import this editor module. those feature module components can be used in our custom editor

Note: editor component will use inside other modules(when the EditorModule was imported) so the editor component must be exported from the EditorModule. “ng g c editor --export” –export flag will be automatic mark EditorModule export or you can make it manually by add-in EditorModule export section.

Check ace available modes with file name start with “mode-” prefix in this directory: https://github.com/ajaxorg/ace-builds/tree/master/src

build ace editor in the editor.component.ts file

import {
  AfterViewInit,
  Component,
  ElementRef,
  EventEmitter,
  Input,
  OnChanges,
  OnInit,
  Output,
  SimpleChanges,
  ViewChild,
} from '@angular/core';

import { Ace, edit } from 'ace-builds';
import 'ace-builds';
import 'ace-builds/src-noconflict/theme-dracula';

@Component({
  selector: 'app-editor',
  templateUrl: './editor.component.html',
  styleUrls: ['./editor.component.scss']
})
export class EditorComponent implements OnInit, AfterViewInit, OnChanges {
  @ViewChild('editor') editorRef!: ElementRef;
  @Output() textChange = new EventEmitter<string>();
  @Input() text!: string;
  @Input() readOnly: boolean = false;
  @Input() mode: string = 'json';
  @Input() prettify: boolean = true;

  editor!: Ace.Editor;
  // All possible options can be found at:
  // https://github.com/ajaxorg/ace/wiki/Configuring-Ace
  options = {
    showPrintMargin: false,
    highlightActiveLine: true,
    tabSize: 2,
    wrap: true,
    fontSize: 14,
    fontFamily: '\'Roboto Mono Regular\', monospace',
  };

  constructor() {}

  ngOnInit(): void { }

  ngAfterViewInit(): void {
    this.initEditor_();
  }

  onTextChange(text: string): void {
    this.textChange.emit(text);
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (!this.editor) {
      return;
    }

    for (const propName in changes) {
      if (changes.hasOwnProperty(propName)) {
        switch (propName) {
          case 'text':
            this.onExternalUpdate_();
            break;
          case 'mode':
            this.onEditorModeChange_();
            break;
          default:
        }
      }
    }
  }

  private initEditor_(): void {
    this.editor = edit(this.editorRef.nativeElement);
    this.editor.setOptions(this.options);
    this.editor.setValue(this.text, -1);
    this.editor.setReadOnly(this.readOnly);
    this.editor.setTheme('ace/theme/dracula');
    this.setEditorMode_();
    this.editor.session.setUseWorker(false);
    this.editor.on('change', () => this.onEditorTextChange_());
  }

  private onExternalUpdate_(): void {
    const point = this.editor.getCursorPosition();
    this.editor.setValue(this.text, -1);
    this.editor.moveCursorToPosition(point);
  }

  private onEditorTextChange_(): void {
    this.text = this.editor.getValue();
    this.onTextChange(this.text);
  }

  private onEditorModeChange_(): void {
    this.setEditorMode_();
  }

  private setEditorMode_(): void {
    this.editor.getSession().setMode(`ace/mode/${this.mode}`);
  }

}

Inside editor.component.html file

Define an element with #editor, where the editor will be rendered in HTML

<div #editor class="app-ace-editor"></div>

Inside editor.component.scss file

The default renders height and width are none. So give editor heigh and weight

.app-ace-editor {
  width: 100%;
  min-height: 400px;
  border: 1px solid gray;
}

Let’s give example for this editor use case

Successfully created generic editor. So in this step, I will show JSON, YAML, TypeScript, and SCSS editor. but you can use all of the mode ace builds provided

#1. Import EditorModule

I am going to use our generic editor in the app component. So Import EditorModule in the app.module.ts file

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { EditorModule } from './editor/editor.module';

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

#2 edit app.component.ts file

In the example, It will have four editors which are JSON, YAML, typescript, and SCSS mode editor. Every mode will use, just need to import their mode modules. So JSON, YAML, TypeScript, SCSS mode modules were imported.

When ace builds any mode, theme extension mode import, The ‘ace-builds’ parent module must be imported. That’s means ‘ace-builds’ module import required

Next Define editor code data properties, which is jsonInputData for JSON, yamlInputData for YAML, appModuleTsData for typescript, scssData form SCSS editor

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

import 'ace-builds';
import 'ace-builds/src-noconflict/mode-json';
import 'ace-builds/src-noconflict/mode-yaml';
import 'ace-builds/src-noconflict/mode-typescript';
import 'ace-builds/src-noconflict/mode-scss';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'angular-code-editor';
  jsonInputData = '';
  yamlInputData = '';
  appModuleTsData = '';
  scssData = '';
}

#3. render code editor in app.component.html file

Our editor selector is app-editor, So we can use <app-editor> it as an element. text multiple bind property.

<app-editor [(text)]="jsonInputData" mode="json" ></app-editor>

For example, multiple code components render

<div style="width: 100%;max-width: 1200px;margin: 0 auto;">
  <div style="display: grid;grid-template-columns: repeat(2, minmax(0, 1fr));gap: 16px;">
    <div>
      <h2>JSON Editor</h2>
      <app-editor [(text)]="jsonInputData" mode="json" ></app-editor>
    </div>
    <div>
      <h2>YAML Editor</h2>
      <app-editor [(text)]="yamlInputData" mode="yaml"></app-editor>
    </div>
    <div>
      <h2>App Module TypeScript Editor <span style="color: blue;">ReadOnly</span></h2>
      <app-editor [(text)]="appModuleTsData" mode="typescript" [readOnly]="true"></app-editor>
    </div>
    <div>
      <h2>Scss Editor</h2>
      <app-editor [(text)]="scssData" mode="scss"></app-editor>
    </div>
  </div>
</div>

Output preview

angular code editor

Related posts

Explained RxJs switchMap operator with example

Sajal Mia

Angular Bootstrap 5 Popup Registration Form

Sajal Mia

Angular Pure Bootstrap 5 Sidenav

Sajal Mia

Angular bootstrap date picker example

Sajal Mia

Implement angular (product) details page with reactive service

Sajal Mia

Angular Date as ago (minutes / hours / days / months / years ago) pipe

Sajal Mia