angular icon indicating copy to clipboard operation
angular copied to clipboard

[Custom Component] - Is it possible to access the current instance of the Formio Component within the custom component?

Open dinbtechit opened this issue 2 years ago • 16 comments

Is it possible to access the current instance of the Formio Component within the custom component?

export class My CustomComponent implements FormioCustomComponent<string> {
  @Input() value: string;
  @Output() valueChange: EventEmitter<string> = new EventEmitter<string>();
  @Output() formioEvent: EventEmitter<FormioEvent> = new EventEmitter<FormioEvent>();
  
  customFunction() {
 
      //  I need to access the instance of Formio Component here.
   }  
 
}

I would like to access the component because the customOptions are not accessible during initialization. Furthermore, dragging the component does not preserve any of the customOptions.

dinbtechit avatar Oct 23 '23 23:10 dinbtechit

I had this question too, and no one answered.

The solution I came up with was to create a global service, and within it there are methods for adding and viewing the main instance.

In the formiobasecomponent component within the createRenderer function, I called the add method of the service I created.

  createRenderer() {
    const Renderer = this.getRenderer();
    const form = (new Renderer(
      this.formioElement ? this.formioElement.nativeElement : null,
      this.form,
      this.getRendererOptions()
    ));

    // adicionado-inicio
    this.formFormioService.setEstruturaComponenteFormularioRender(form);
    // adicionado-fim

    return form.instance;
  }

In custom components, I injected this global service and viewed the instance that way.

It worked, I hope it helps you.

adhonay avatar Dec 14 '23 13:12 adhonay

Can you explain more @adhonay how you are storing this and how you will get the same instance? It will be very helpful.

pradeepsonisoni avatar Dec 15 '23 08:12 pradeepsonisoni

Você pode explicar mais@adhonaycomo você está armazenando isso e como obterá a mesma instância? Será muito útil.

Ignore the main and secondary instance part, in your case it will probably only be 1 instance. I did it this way, because I am using modals from one instance within another.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { ConstantesSistemaFormio } from '../constantes/constantes-sistema-formio';

@Injectable({
  providedIn: 'root'
})
export class FormFormioService {

  private estruturaComponenteFormularioRenderInstanciaPrincipal = new BehaviorSubject<object>(undefined);
  private estruturaComponenteFormularioRenderInstanciaSecundariaGrid = new BehaviorSubject<object>(undefined);

  constructor() { }

  private quantidadeInstaciasFormioRender(): number {
    return document.querySelectorAll(ConstantesSistemaFormio.tceFormio)?.length;
  }

  public existeInstanciaRenderFormio(): boolean {
    return this.quantidadeInstaciasFormioRender() > 0
  }

  public existeInstanciaSecundariaGridAberta(): boolean {
    return this.quantidadeInstaciasFormioRender() === 2
  }

  public setEstruturaComponenteFormularioRender(instancia) {
    if (instancia && this.quantidadeInstaciasFormioRender() <= 1) {
      this.estruturaComponenteFormularioRenderInstanciaPrincipal.next(instancia);
    } else if (instancia && this.quantidadeInstaciasFormioRender() === 2) {
      this.estruturaComponenteFormularioRenderInstanciaSecundariaGrid.next(instancia);
    }
  }
  public getEstruturaComponenteFormularioRenderInstanciaPrincipal(): object {
    return this.estruturaComponenteFormularioRenderInstanciaPrincipal.value;
  }

  public getEstruturaComponenteFormularioRenderInstanciaSecundariaGrid(): object {
    return this.estruturaComponenteFormularioRenderInstanciaSecundariaGrid.value;
  }
}

adhonay avatar Dec 15 '23 12:12 adhonay

Can you show the createRenderer class aswell how you are extending the class.?

pradeepsonisoni avatar Dec 15 '23 12:12 pradeepsonisoni

Você pode explicar mais@adhonaycomo você está armazenando isso e como obterá a mesma instância? Será muito útil.

Ignore the main and secondary instance part, in your case it will probably only be 1 instance. I did it this way, because I am using modals from one instance within another.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { ConstantesSistemaFormio } from '../constantes/constantes-sistema-formio';

@Injectable({
  providedIn: 'root'
})
export class FormFormioService {

  private estruturaComponenteFormularioRenderInstanciaPrincipal = new BehaviorSubject<object>(undefined);
  private estruturaComponenteFormularioRenderInstanciaSecundariaGrid = new BehaviorSubject<object>(undefined);

  constructor() { }

  private quantidadeInstaciasFormioRender(): number {
    return document.querySelectorAll(ConstantesSistemaFormio.tceFormio)?.length;
  }

  public existeInstanciaRenderFormio(): boolean {
    return this.quantidadeInstaciasFormioRender() > 0
  }

  public existeInstanciaSecundariaGridAberta(): boolean {
    return this.quantidadeInstaciasFormioRender() === 2
  }

  public setEstruturaComponenteFormularioRender(instancia) {
    if (instancia && this.quantidadeInstaciasFormioRender() <= 1) {
      this.estruturaComponenteFormularioRenderInstanciaPrincipal.next(instancia);
    } else if (instancia && this.quantidadeInstaciasFormioRender() === 2) {
      this.estruturaComponenteFormularioRenderInstanciaSecundariaGrid.next(instancia);
    }
  }
  public getEstruturaComponenteFormularioRenderInstanciaPrincipal(): object {
    return this.estruturaComponenteFormularioRenderInstanciaPrincipal.value;
  }

  public getEstruturaComponenteFormularioRenderInstanciaSecundariaGrid(): object {
    return this.estruturaComponenteFormularioRenderInstanciaSecundariaGrid.value;
  }
}

I am not able to get instance when i invoke setInstance(), or either calling createRenderer() from constructor in my custom component using reference from code snippet you provided , so could you please provide your custom component with the proper place where you are getting the instance ? or you can send across the files which have the running code. (Note: We have tried all possible ways to get instance and we have angular cli 16 as a mandatory platform.)

Sahil-Sayyed avatar Dec 15 '23 12:12 Sahil-Sayyed

By extending MaterialComponent class i am able to get the instance of the custom component with setInstance() in one project but in another project without using angular material and considering typescript and angular(angular material excluded ) setup, which class i can extend/implement to achieve similar behavior?

Sahil-Sayyed avatar Dec 15 '23 13:12 Sahil-Sayyed

Hi @dinbtechit

Did you find any solution?

pradeepsonisoni avatar Jan 10 '24 14:01 pradeepsonisoni

@pradeepsonisoni - I have not. I think the custom component has some issues with the way how it implemented updating the component value. Also, starting from version Formio/angular version 6.0.0 the custom components are being deprecated all together. So it may be worth forking the custom component code and adding it to your own angular project. That way, you will have more control over the code or at least that's what I am going to be doing to fix this issue.

dinbtechit avatar Jan 10 '24 14:01 dinbtechit

@dinbtechit Thank you for quick reply.

pradeepsonisoni avatar Jan 10 '24 14:01 pradeepsonisoni

Você pode mostrar a classe createRenderer também como está estendendo a classe?

createRenderer is a method already implemented by formio itself in the formiobasecomponent.ts component

adhonay avatar Jan 11 '24 12:01 adhonay

@adhonay If you are able to get the instance. you can share the code how you are implementing the FormioCustomComponent class. let us know because We are not getting the instance.

pradeepsonisoni avatar Jan 11 '24 12:01 pradeepsonisoni

@adhonaySe você conseguir obter a instância. você pode compartilhar o código como está implementando a classe FormioCustomComponent . deixe-nos saber porque não estamos recebendo a instância.

FormioCustomComponent here is a simple interface, created in the Angular template.

export interface  FormioCustomComponent<T> {
  value: T; // Should be an @Input
  valueChange: EventEmitter<T>; // Should be an @Output
  disabled: boolean;
  formioEvent?: EventEmitter<FormioEvent>; // Should be an @Output
}

You don't need anything more than:

1- Create the service as I sent above.

2- Insert the line below into the createRenderer function in formiobasecomponent.ts to send the instance. this.formFormioService.setEstruturaComponenteFormularioRender(form);

3- In your CustomComponent, inject the service into the constructor and then wherever you want in the component, use the line: let instanciaRender = this.formFormioService.getEstruturaComponenteFormularioRenderInstanciaPrincipal();

adhonay avatar Jan 11 '24 17:01 adhonay

@adhonay If we have two instance of the same custom component. Is it working with two instance? I don't think so.

pradeepsonisoni avatar Jan 11 '24 17:01 pradeepsonisoni

I had this question too, and no one answered.

The solution I came up with was to create a global service, and within it there are methods for adding and viewing the main instance.

In the formiobasecomponent component within the createRenderer function, I called the add method of the service I created.

  createRenderer() {
    const Renderer = this.getRenderer();
    const form = (new Renderer(
      this.formioElement ? this.formioElement.nativeElement : null,
      this.form,
      this.getRendererOptions()
    ));

    // adicionado-inicio
    this.formFormioService.setEstruturaComponenteFormularioRender(form);
    // adicionado-fim

    return form.instance;
  }

In custom components, I injected this global service and viewed the instance that way.

It worked, I hope it helps you.

@adhonay - isn't the formiobasecomponent class part the npm package, how did you manage to update the createRenderer() method to inject the code - > this.formFormioService.setEstruturaComponenteFormularioRender(form);?

dinbtechit avatar Jan 11 '24 17:01 dinbtechit

I had this question too, and no one answered. The solution I came up with was to create a global service, and within it there are methods for adding and viewing the main instance. In the formiobasecomponent component within the createRenderer function, I called the add method of the service I created.

  createRenderer() {
    const Renderer = this.getRenderer();
    const form = (new Renderer(
      this.formioElement ? this.formioElement.nativeElement : null,
      this.form,
      this.getRendererOptions()
    ));

    // adicionado-inicio
    this.formFormioService.setEstruturaComponenteFormularioRender(form);
    // adicionado-fim

    return form.instance;
  }

In custom components, I injected this global service and viewed the instance that way. It worked, I hope it helps you.

@adhonay - isn't the formiobasecomponent class part the npm package, how did you manage to update the createRenderer() method to inject the code - > this.formFormioService.setEstruturaComponenteFormularioRender(form);?

I cloned the git project and started implementing custom components from it.

You can modify the createRenderer method because you have direct access to it.

https://github.com/formio/angular/blob/master/projects/angular-formio/src/FormioBaseComponent.ts

Line 108

adhonay avatar Jan 11 '24 19:01 adhonay

Thanks @adhonay, that makes sense now. Actually, a better approach would be to fork the custom component and modify the logic there.

https://github.com/formio/angular/tree/5.5.x/projects/angular-formio/src/custom-component

dinbtechit avatar Jan 11 '24 19:01 dinbtechit