0

I'm trying to reduce code repetition in my code base, where I have the following pattern in my HTMLs:

<card-component>
    <card-header>{{entityFormName}}</card-header>
    <card-body>
        <form (ngSubmit)="onSubmit()" id="formId" [formGroup]="entityForm">
               <!-- <ng-content></ng-content> -->
               <!-- Here I have a bunch of custom form content/formatting -->
        </form>
    </card-body>
    <card-footer>
        <!-- button outside of form -->
        <button type="submit" form="formId" [disabled]="entityForm.invalid || loading">{{submitButtonText}}</button>
        <!-- loading icon spinner -->
    </card-footer>
</card-component>

basically, I have several CRUD-type forms that follow this pattern, and I want to keep it all in a separate component so reduce repetition. I have an abstract component-like class that implements onSubmit(), creates entityForm and controls loading. The classes that extend from this abstract class will also implement some custom behavior for their own forms.

The thing is: how do I send the entityForm to whatever component is implementing this form, so that I can actually create it? Is it even possible?

Alternatively, am I approaching this the wrong way? Perhaps there's a better/different pattern to follow in order to reduce code repetition here?

Thank you in advance.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Badashi
  • 1,124
  • 9
  • 18
  • it sounds like you just want to make this a component that you can plug in where ever and have input parameters of an FormGroup called entityForm and a few strings like entityFormName and submitButtonText.? You don't need to do all this abstract class / inheritance stuff. However, I think in practice that you'll find this is all less reusable and more restrictive than you're imagining it will be. – bryan60 Nov 07 '17 at 19:05
  • My intention is NOT flexibility, but reduce code repetition. If I change the loading icon, I don't want to have to change it for every form. If it is decided that the form submit callback should change, it should be reflected on every form of this type. This is about consistency - it's not intended to be slapped on "any" page. – Badashi Nov 08 '17 at 11:31
  • Good angular is striking a balance between flexibility and being DRY. There are several other options that you’re missing such as just making the pieces of it reusable components like the loader and creating a loader service. Now you’re in a situation where if one of the forms becomes atypical, you can’t edit one without breaking them all. The likely outcome is that this framework will be too restrictive and you end up with subpar ux. This is just my perspective as an enterprise angular engineer. Do whatever you want. – bryan60 Nov 08 '17 at 11:50

1 Answers1

0

I figured it out.

On my form-wrapper component, I have the following:

export class AbstractFormComponent {
  @Input()
  formTitle: string;

  @Input()
  component: any;

  get entityForm(): FormGroup { return this.component.entityForm; }

  get toasterConfig(): ToasterConfig { return this.component.toasterConfig; }

  get submitButtonText(): string { return this.component.submitButtonText; }

  _internalSubmit() {
    this.component.onSubmit();
  }

}

Then, within each of my custom forms I can use:

<custom-form formTitle="Form Title" [component]="this">
    <!-- form inputs, grid and etc -->
</custom-form>

Like this, my forms will follow a desired pattern while the internal behavior is controlled by the inner component.

There is a caveat here, I set my component to be an any object because I can't declare it as a generic type(ie AbstractEntityComponent<E extends Entity, S extends Service<E>>) without making the wrapper component generic by itself.

Badashi
  • 1,124
  • 9
  • 18