ngx-formly / ngx-formly

📝 JSON powered / Dynamic forms for Angular
https://formly.dev
MIT License
2.79k stars 560 forks source link

Polymorph #925

Closed thorgod closed 6 years ago

thorgod commented 6 years ago

I'm submitting a ... (check one with "x")

[ ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[X] support request

Trying to create a field type that will polymorph into a different field type based on model data. I want to use reuse the components in ngx-formly ui-bootstrap, is this even possible for each type?

Example: model.cool = Yes , other field is a input model.cool = No , other field is a select model.cool = Maybe , other field is a number input

Question: Would a switch statement be the best way?

<div [ngSwitch]="type">

    <ng-template *ngSwitchCase="'number'">
        <input type="number" [formControl]="formControl" [style.width]="to.width" class="form-control" [formlyAttributes]="field"
            [class.is-invalid]="showError" [attr.disabled]="disable">
    </ng-template>

    <ng-template *ngSwitchCase="'select'">
        <select class="form-control" [formControl]="formControl" [class.is-invalid]="showError" [formlyAttributes]="field">
            <option *ngIf="to.placeholder" value="">{{ to.placeholder }}</option>
            <ng-container *ngFor="let item of selectOptions | async">
                <optgroup *ngIf="item.group" label="{{item.label}}">
                    <option *ngFor="let child of item.group" [value]="child[valueProp]" [disabled]="child.disabled">
                        {{ child[labelProp] }}
                    </option>
                </optgroup>
                <option *ngIf="!item.group" [value]="item[valueProp]" [disabled]="item.disabled">{{ item[labelProp] }}</option>
            </ng-container>
        </select>
    </ng-template>

    <input *ngSwitchDefault [style.width]="to.width" [type]="type" [formControl]="formControl" class="form-control" [formlyAttributes]="field"
        [class.is-invalid]="showError" [attr.disabled]="disable">

</div>
...

  get type() {
    if(model.cool === 'Yes'){ return 'number';; } 
    if(model.cool === 'No') { return 'select';}

    if(model.cool === 'Maybe') { return 'default';}

    return this.to.type || 'text';
  }
aitboudad commented 6 years ago

there is another better way but I encountered several issues which it need some extra work to make it work properly see https://stackblitz.com/edit/angular-2tdkxm

thorgod commented 6 years ago

Awesome! Thanks I will look deeper into it tomorrow, see if I can work out the kinks (issues) in your example.

thorgod commented 6 years ago

One of the kinks I cant seem to work out is the multiple wrappers. I am getting two wrappers one for the component and one for the child component.

Stackblitz image

In my application image

I use delete wrapper but dont think that is right

 ngOnInit() {
    delete this.field.wrappers;

https://stackblitz.com/edit/angular-2tdkxm-fdj7hr?file=app/custom-input.component.ts

aitboudad commented 6 years ago

we should add a way to skip preWrapper and postWrapper https://github.com/formly-js/ngx-formly/blob/master/src/core/src/components/formly.field.ts#L111, maybe when passing a false value

thorgod commented 6 years ago

I updated my comment I think I found a way, by deleting the wrappers for one of parent component on ngInt, but skipping wrapper might be helpful.

 ngOnInit() {
    delete this.field.wrappers;

https://stackblitz.com/edit/angular-2tdkxm-fdj7hr?file=app/custom-input.component.ts

thorgod commented 6 years ago

Actually you are correct we would need something like that to skip preWrapper and postWrapper.

thorgod commented 6 years ago

@aitboudad For the core should I add a poperty it to the FormlyTemplateOptions for skipping preWrapper/postWrapper?

export interface FormlyTemplateOptions {
...
skipWrappers?: boolean;
...
}

Then look at FormlyTemplateOptions when adding trying to add pre-wrapper/post-wrapper to see if it should skip?

and in core

    if(this.field.templateOptions.skipWrappers) {
      return [];
    }

    return [...preWrappers, ...this.field.wrappers, ...postWrappers];
thorgod commented 6 years ago

So I submitted a pull request let me know what you think.

aitboudad commented 6 years ago

fixed in 3.0.3, left one thing: re-render field when type is changed (if you have a better way to manage it PR is welcomed :) ), for the moment using ngSwitch would solve the issue see https://stackblitz.com/edit/angular-2tdkxm-gr5dgk

thorgod commented 6 years ago

Thanks! Alright sounds good I will give it a try.

(My system is in a bad state tried to install angular 6 but got a bunch of errors/issues now, reverting back angular 5.2.5) I will try upgrading again in a few weeks.

thorgod commented 6 years ago

@aitboudad Awesome seems to work! Thank you for your help! Alt Text