angularangular-dynamic-componentsangular-event-emitterrecursive-component

Recursive Dynamic Angular Form; EmitEvent from Deeper Child to Root (Parent Component)


Hi I'm newbie and I'm creating a project with a Reactive Form; based on Recursive Component that creates Dynamic Form from JSON file. The Sources

This is an adaptation from Ionic based on Creating Dynamic Angular Forms with JSON

I Adapted the Recursive version procedures and other changes! My code is located in Stackblitz.

My component SelectsComponent located in the selects.component.ts file, has the selector selector: 'selects', I have:

  @Output() addControl = new EventEmitter<JsonFormControls>();

and the method

  public onSelectChange(event: MatSelectChange) {
    console.log(this.form.value);
    console.log('parent:' + this.parentControl || 'root');
    this.control.value = event + '';
    if (this.control.children) {
      this.recursiveConcealer(this.control.children);
      const child = this.control.children.find(
        (child) => child.value === event + ''
      );
      this.newControl(child);
      if (child.siblings) {
        for (let sibling of child.siblings) {
          this.newControl(sibling);
        }
      }
      //Emit Event to Root
      this.addControl.emit(child);
    }
  }

When the onSelectChange method is called, then emit is performed this.addControl.emit(child);

In the template selects.component.html file, I have this code:

  <ng-container *ngFor="let child of control?.children">
    <div fxFlex="100%">
      <selects
        *ngIf="child.type === 'select'"
        [control]="child"
        [parentControl]="control"
        [formBuilder]="formBuilder"
      ></selects>
    </div>
  </ng-container>

I have the JsonFormComponent located in the json-form.component.ts file, with this method:

  public onAddControl(addControlEvent: JsonFormControls) {
    this.addControl(addControlEvent);
    addControlEvent.visible = true;
  }

In it's template json-form.component.html file, with this code:

<selects
  *ngIf="control.type === 'select'"
  [control]="control"
  [visible]="true"
  (addControl)="onAddControl($event)"
  [formBuilder]="myFormBuilder"
></selects>

The problem raise when some Child-component (SelectsComponent) is not a direct son of the Root Parent-Component (JsonFormComponent), maybe is a grandson or depeer relation, as:

(level 0)JsonFormComponent -> (level 1)SelectsComponent -> (level 2)SelectsComponent -> (level 3)SelectsComponent

How re-emit from SelectsComponent child to SelectsComponentparent.

QUESTIONS:

  1. How to send or Emit Event from (level 3)SelectsComponent to (level 0)JsonFormComponent ?
  2. How to send some Event of Acknowledgment from (level 0)JsonFormComponent to (level 3)SelectsComponent?

Thanks in advance!

I appreciate answer not based on services that I know could be an alternative.

EDIT:

enter image description here

What is the Console.log(...) expected?

console.log(`parent: ${this.parentControl?this.parentControl.name:'root'} -> control.name: ${this.control.name} -> Emitter: ${e?.name}` );

The first(or root) component is company

Something like:

parent: root -> control.name: company -> {Here the last node child}.

According to the click in the previous image

1st click (first level):

parent: root -> control.name: company -> Emitter: Petitioner (C2 -> P2).

2nd click (second level):

parent: root -> control.name: company -> Emitter: Service (C2 -> P2 -> S2).

Example: 3nd click (third level):

parent: root -> control.name: company -> Emitter: Request (C2 -> P2 -> S2 -> R2).

The console.log (or future operations) only will be performed in the context of the root (that is company).


Solution

  • Here's the updated solution in https://stackblitz.com/edit/angular-ivy-6kccps

    1.How to send or Emit Event from (level 3)SelectsComponent to (level 0)JsonFormComponent ?

    Listen to the addControl event emitted by child selects component in the selects.component.html

      <selects
        *ngIf="child.type === 'select'"
        [control]="child"
        [parentControl]="control"
        [formBuilder]="formBuilder"
        (addControl)="handleAddControl($event)"
      ></selects>
    

    Emit the event

    handleAddControl(data: any) {
        this.addControl.emit(data);
      }
    

    This will send events from every instance of SelectsComponent to be bubbled up to the top JsonFormComponent

    2.How to send some Event of Acknowledgment from (level 0)JsonFormComponent to (level 3)SelectsComponent?

    I am not really sure what is needed in terms of sending an acknowledge. However, I assume upon receiving an acknowledge in the original instance you would be performing some action, like updating some property. It can be achieved by adding a callback to the event payload as follows: selects.component.ts

     //Emit Event to Root
    public onSelectChange(event: MatSelectChange) {
    ...
    ...
    ...
     const data = {child,
      callback: () => {
        console.log('Executing callback for control with label', this.control.name)
      }
    }   
    
    this.addControl.emit(data);
    

    Execute the callback in json-form.component.ts

      public onAddControl(addControlEvent: any) {
        if (addControlEvent.callback) {
          addControlEvent.callback();
        }
        this.addControl(addControlEvent);
        addControlEvent.visible = true;
      }