formio / angular

JSON powered forms for Angular
https://formio.github.io/angular-demo
MIT License
640 stars 469 forks source link

Register a custom Component in Formio 4.x.x #397

Closed donax3l closed 2 months ago

donax3l commented 4 years ago

Hi, how can i register a new custom component in formiojs 4.x.x? I've tried with "Formio.registerComponent('custom', CustomComponent);" but i've received this error: "registerComponent don't exist on typeof Formio". Can you help me?

merobal commented 4 years ago

Hi, I'll add a step-by-step guide this week.

merobal commented 4 years ago

@donax3l you can find the doc here: https://github.com/formio/angular-formio/wiki/Custom-Components-with-Angular-Elements

jerocon commented 4 years ago

Hi, is there any way to get the values ​​that have been entered in the 'editForm' from the Angular component?

merobal commented 4 years ago

Yes, I extended the documentation with that part: https://github.com/formio/angular-formio/wiki/Custom-Components-with-Angular-Elements#options

jerocon commented 4 years ago

Thanks for the quick reply!!

We need to get the values ​​in the ngOnInit() event but the values ​​are not available at that time.

We have also seen that multiple instances of the component are being created in the Form Builder; a first instance with ngOnInit and ngOnDestroy and another second instance only with ngOnInit.

This does not happen when the Form Renderer is accessed

Thank you!!

merobal commented 4 years ago

Yes, I added an other note about this case: https://github.com/formio/angular-formio/wiki/Custom-Components-with-Angular-Elements#lifecycle-of-the-component

You can't always have the value inside ngOnInit as it might be bound later.

Also, the builder re-renders the component after every little update, that's why you see it's being re-initialized.

jerocon commented 4 years ago

When we access the component edition, the ngOnInit event is called again (this is normal)

The problem is when the Form Builder is first accessed, the two instances of the component are launched.

If we debug, you can see how you first 'paint' the form with the components (first call to ngOnInit). Then complete it with the menu on the left by moving the component to the right (a call is made to ngOnDestroy and creates another component instance by calling ngOnInit again)

donax3l commented 4 years ago

Hi, thank you! It's great the opportunity to add Angular Component inside FOrmio schemas. I've tried to define a custom component that extends "NestedComponent" with this import: "import { NestedComponent } from "formiojs/types/components/_classes/nested/nestedComponent"; but i receive this error: "Module not found: Error: Can't resolve 'formiojs/types/components/_classes/nested/nestedComponent'",, how can i solve this?

merobal commented 4 years ago

Try this:

import { Components } from 'angular-formio';

const NestedComponent = Components.components.nested;

And extend NestedComponent.

jerocon commented 4 years ago

Hi again!!

I will try to explain an example in the case of multiple calls to the OnInit event of a customComponent. The example is with the OnInit event but the same happens with functions of the type @Input () set myOption (v: string) {}

We have a FormBuilder loaded with [form] = {}. When you click on a button, the [form] property is loaded with the content of the form.

In the component we have added a console.log in the OnInit event. In the browser console we can see the following lines:

=  ngOnInit
=  ngOnInit
=  ngOnInit

We clean the browser console and click on the button that loads the form data. The result is as follows:

= ngOnInit
= ngOnInit
= ngOnInit
= ngOnInit
= ngOnInit

This behavior makes it difficult to have control in the component to perform data loading.

When capturing the form data in a Form Renderer the behavior is as expected; only one call to the OnInit event is made.

donax3l commented 4 years ago

Try this:

import { Components } from 'angular-formio';

const NestedComponent = Components.components.nested;

And extend NestedComponent.

Hi, thank you, this seems to work.

My custom component is written in a single class like in "CheckMatrix" but i extend NestedComponent as said before. I have implemented the setValue() and getValue() functions in my component, but they aren't invoked. I have declared them in this way: setValue(value, flags) { ... } getValue() {... }

donax3l commented 4 years ago

Any help?

merobal commented 4 years ago

@jerocon try using a cache layer (service) to avoid duplicated requests with the same query params for this case. Since you're building a form, it may re-render itself multiple times.

Our example:

  private _cache = new Map<string, any>();

  private cachedRequest<R>(id: string, call: () => Observable<R>, extraCacheValidationParams?: any): Observable<R> {
    const key = id + JSON.stringify(extraCacheValidationParams);
    if (this._cache.has(key)) {
      return of(this._cache.get(key));
    } else {
      return call().pipe(tap(response => this._cache.set(key, response)));
    }
  }

  queryValidation(body: DTOQueryValidation, preview: boolean): Observable<QueryValidationResponse> {
    return this.cachedRequest('queryValidation', () => this.apiService.queryValidation(body, preview), {
      body,
      preview,
    }).pipe(
      map(response => ({
        success: response[0].errorMessage === undefined,
        preview: response.map(transformCodedValueContract),
      })),
    );
  }
merobal commented 4 years ago

@donax3l can you please provide a repro to check?

donax3l commented 4 years ago

Thank you @merobal for your helpful example in the "angular-formio-custom-demo" project. In the new new version of Formio how am i supposed to instantiate elements inside the "components" attribute? In formio 3.x.x my custom components behaved like editGrid (estending NestedComponent and using lodash and the super.createComponent(...) ). But now i'm creating the custom component using angular components like in you; so once read elements inside the "components" attribute, how can i instantiate them?

ChayanNath commented 1 year ago

@merobal can you please tell, how can we validate the custom component, I have custom requirements when it comes to validating the custom component and the validation should occur on change event?

lane-formio commented 2 months ago

It seems the original issue brought up here has been resolved and the rest of the thread has gone stale since then. I'm going to go ahead and close the issue, but please feel free to open a new issue or ask me to re-open if the original issue still isn't resolved.