javascriptangularace-editor

facing problems getting the native element of child component instances in Angular using Ace editor


I am trying to build a page that has two JSON code editors placed side by side that can show two different JSON code snippets and can also be configured differently if needed (i.e. separate theme, settings, size and errors) using ACE Editor.

I've figured most of the working by now. However, I am trying to re-use the same code-editor shared component as two child instances in single parent component where I want to display these two instances as two editors displaying different JSON values and other params, this is where it is not working out for me.

Here is the code:

parent.component.html

<div class="projects">
    <!-- layer to display two editors side by side -->
    <div class="row no-margin">
        <div class="col">
            child editor 1
            <childComponent [template]="childTemplate1" jsonValue="{name:joe}" #childEditor1></childComponent>
        </div>
        <div class="col">
            child editor 2
            <childComponent [template]="childTemplate2" jsonValue="{name:bob}" #childEditor2></childComponent>
        </div>
    </div>
</div>

<ng-template #childTemplate1>
    <div class="app-ace-editor" #childEditor1 style="height: 80vh;"></div>
</ng-template>

<ng-template #childTemplate2>
    <div class="app-ace-editor" #childEditor2 style="height: 80vh;"></div>
</ng-template>

parent.component.ts

  @ViewChild('childEditor1') private childEditor1: ElementRef<HTMLElement>;
  @ViewChild('childEditor2') private childEditor2: ElementRef<HTMLElement>;
  @ViewChild(SharedCodeEditorChildComponent) private childEditor: SharedCodeEditorChild;

  constructor() { }

  ngOnInit(): void {
  }

  /* invoke ace editor's method based on the child editor instances */
  ngAfterViewInit(): void {
    this.childEditor.invokeEditor(this.childEditor1);
    this.childEditor.invokeEditor(this.childEditor2);
  }

sharedCodeEditorChild.component.html

<ng-container *ngTemplateOutlet="template"></ng-container>

sharedCodeEditorChild.component.ts

  @Input() template: TemplateRef<any>;
    
  constructor() { }

  ngAfterContentInit(): void {
  
  }

  ngOnInit(): void {
  
  }
  
  ngAfterViewInit(): void {
  
  }

  invokeEditor(elem:any) {
    ace.config.set("fontSize", this.fontSize + 'px');
    ace.config.set('basePath', 'https://unpkg.com/ace-builds@1.4.12/src-noconflict');
    this.aceEditor = ace.edit(elem.nativeElement); // this returns undefined and code further doesnt work
  }

What I tried

I am trying since yesterday evening before asking this question here with different approaches like:

using ContentChildren, ViewContainerRef, ViewChildren and even dynamically setting template reference using createEmbeddedView(child.nativeElement).

Tried researching on chatGPT, and stackoverflow but unfortunately nothing works for me.

What I want

In sharedCodeEditorChild.component.ts I want this line this.aceEditor = ace.edit(elem.nativeElement); to correctly return the child's native element depending on which child instance I am calling, so that both the editors can show different two different JSON values that are passed from parent, as well as they can also be configured separately using a single sharedCodeEditorChildComponent.

It will be very helpful and save my day, if you can guide me in the right direction. Thank you.

UPDATE: if anyone is looking for an answer then please take a look here https://stackblitz-starters-gpzdau.stackblitz.io, this is working now as per the answer provided by Murli below.


Solution

  • You have the template ref #childEditor1 and #childEditor2 duplicated in two places! remove this and try to initialize

    <div class="projects">
        <!-- layer to display two editors side by side -->
        <div class="row no-margin">
            <div class="col">
                child editor 1
                <childComponent [template]="childTemplate1" jsonValue="{name:joe}" #childEditor1></childComponent>
            </div>
            <div class="col">
                child editor 2
                <childComponent [template]="childTemplate2" jsonValue="{name:bob}" #childEditor2></childComponent>
            </div>
        </div>
    </div>
    
    <ng-template #childTemplate1>
        <div class="app-ace-editor" #codeEditor1 style="height: 80vh;"></div>
    </ng-template>
    
    <ng-template #childTemplate2>
        <div class="app-ace-editor" #codeEditor2 style="height: 80vh;"></div>
    </ng-template>
    

    parent.html

    @ViewChild('childEditor1') private childEditor1: SharedCodeEditorChild;
    @ViewChild('childEditor2') private childEditor2: SharedCodeEditorChild;
    
    @ViewChild('codeEditor1') private codeEditor1: ElementRef<HTMLElement>;
    @ViewChild('codeEditor2') private codeEditor2: ElementRef<HTMLElement>;
    
      constructor() { }
    
      ngOnInit(): void {
      }
    
      /* invoke ace editor's method based on the child editor instances */
      ngAfterViewInit(): void {
        this.childEditor1.invokeEditor(this.codeEditor1);
        this.childEditor2.invokeEditor(this.codeEditor2);
      }