The AOC Framework
github logodiscord logo

FRONTEND

<aoc-from> #

This component is used to display a form, i.e., for the creation and editing of models.

Visually, forms are placed within windows, although this is transparent to the programmer.

Inputs #

Name Type Description
modelConfig AocModelConfig The model-config instance that will be used internally
formGroup FormGroup The FormGroup associated with the form
restOptions AocRestOptions Loading options for the REST API
saveButtonWithTypeSubmit boolean (default: false) Useful for forms that you want to submit by pressing the enter key
newModelTitle string, Observable, Promise, function Provide the window title of the form when creating a new model
editModelTitle string, Observable, Promise, function Provide the window title of the form when editing a new model
saveModel 'persist' or 'persistWithTask' The persist mode does not close the window until the server has responded (by default), persistWithTask runs the action in the background to avoid timeouts on the xhr call

Outputs #

Name Type Description
aocDynFormGroup AocDynFormGroup Emits the dynamic form configuration

Note: AocDynFormGroup is a configuration that allows setting values or states to a control at a given moment, even creating the control if necessary. When opening a form window, such information can be sent, such as setting a value for a dependency (from another model).

Example #

A form with a text field (Name). The aoc-form component should be structured as follows. The body template and the aoc-ui-form-page and aoc-ui-form-row elements are necessary.

<aoc-form [formGroup]="formGroup" [modelConfig]="modelConfig">
  <ng-template aocFormTemplate="body">
    <aoc-ui-form-page>
      <aoc-ui-form-row>
        <input aocUiInputText aocUiFormField="Name" [formControlName]="StoreClass.field.NAME">
      </aoc-ui-form-row>
    </aoc-ui-form-page>
  </ng-template>
</aoc-form>

The StoreClass attribute refers to the Store model and is used to link the model fields to the form through the formControlName.

export default class StoreFormComponent {
  protected StoreClass = Store;
  protected modelConfig = inject(StoreModelConfig);
  protected formGroup = new FormGroup<AocFormGroupType<Store>>({
    name: new FormControl(null, Validators.required)
  });
}

See the full example at quest-client/src/app/features/schemas/items/store/store-form.component.ts

Advanced Example #

This is an example of a tabbed form (it has 3 tabs: Data, Contact, Files), with field groupings, subforms, and subcomponents.

<aoc-form [modelConfig]="modelConfig" [formGroup]="formGroup" [restOptions]="restOptions">
  <ng-template aocFormTemplate="body">
    <aoc-ui-tab-panel>
      <aoc-ui-tab-panel-content header="Data">
        <aoc-ui-form-page>
          <aoc-ui-form-row>
            <input aocUiInputText aocUiFormField="Code" [formControlName]="CustomerClass.field.CODE" placeholder="Autogenerated on server if empty...">
            <app-language-autocomplete aocUiFormField="Language" [formControlName]="CustomerClass.entity.LANGUAGE"></app-language-autocomplete>
            <app-gender-autocomplete aocUiFormField="Gender" [formControlName]="CustomerClass.entity.GENDER"></app-gender-autocomplete>
          </aoc-ui-form-row>
          <app-legal-data-template-subform [formGroupName]="CustomerClass.embedded.LEGAL_DATA_TEMPLATE"></app-legal-data-template-subform>
          <aoc-ui-form-row>
            <input aocUiInputText aocUiFormField="Trade name" [span]="18" [formControlName]="CustomerClass.field.TRADE_NAME" placeholder="Just for companies, not mandatory...">
            <aoc-ui-datetime-picker aocUiFormField="Birthdate" [formControlName]="CustomerClass.field.BIRTHDATE" mode="date" placeholder="mm/dd/y"></aoc-ui-datetime-picker>
          </aoc-ui-form-row>
          <app-address-template-subform [formGroupName]="CustomerClass.embedded.ADDRESS_TEMPLATE"></app-address-template-subform>
        </aoc-ui-form-page>
      </aoc-ui-tab-panel-content>
      <aoc-ui-tab-panel-content header="Contact">
        <aoc-ui-form-page>
          <aoc-ui-form-fieldset title="Main Contact Info">
            <app-contact-template-subform></app-contact-template-subform>
          </aoc-ui-form-fieldset>
          <aoc-ui-form-row aocUiFormRowHeight="stretch">
            <app-contact-grid-field aocUiFormField="Additional Contacts" [formControlName]="CustomerClass.collection.CONTACT"></app-contact-grid-field>
          </aoc-ui-form-row>
        </aoc-ui-form-page>
      </aoc-ui-tab-panel-content>
      <aoc-ui-tab-panel-content header="Files">
        <aoc-ui-form-page>
          <aoc-ui-form-row aocUiFormRowHeight="stretch">
            <app-file-grid-field
              aocUiFormField="Customer files (documents, contracts...)"
              [formControlName]="CustomerClass.collection.FILE"
              [fileParentClass]="CustomerClass"
              fileDirectory="Customers"
            ></app-file-grid-field>
          </aoc-ui-form-row>
        </aoc-ui-form-page>
      </aoc-ui-tab-panel-content>
    </aoc-ui-tab-panel>
  </ng-template>
</aoc-form>

In the definition of the formGroup we have examples of validators, embedded fields (subobject), inline fields (fields at the same level), and collections (relations in PostgreSQL). Both embedded and inline fields could be expressed as inheritances in PostgreSQL.

The restOptions indicate that certain relations need to be loaded for the form to display data correctly (for example, when editing).

this.formGroup = new FormGroup<AocFormGroupType<Customer>>({
  code: new FormControl(null, AocUiValidators.positiveNumber(0)),
  trade_name: new FormControl(null),
  birthdate: new FormControl(null),
  gender: new FormControl(this.questDefaultsService.gender),
  language: new FormControl(this.questDefaultsService.language, Validators.required),
  legalDataTemplate: this.questUtilsService.addControlsForLegalDataTemplate(), // Embedded
  addressTemplate: this.questUtilsService.addControlsForAddressTemplate(), // Embedded
  contactCollection: new FormControl([]),
  fileCollection: new FormControl([])
});

// Inline
this.questUtilsService.addControlsForContactTemplate(this.formGroup);

this.restOptions = {
  populate: {
    language: true,
    gender: true,
    contactCollection: true,
    fileCollection: true
  }
}

Complete example at quest-client/src/app/features/schemas/customers/customer/customer-form.component.ts

Api #

Full definition in the API.

<aoc-subform> #

This component is used for subforms (forms within forms).

In the advanced example, it would be used in <app-legal-data-template-subform>.

The template automatically receives the formGroup by context, and in this case, we assign it to the formGroup directive of the new row in question aoc-ui-form-row that we add to the parent form.

<aoc-subform>
  <ng-template let-formGroup>
    <aoc-ui-form-row [formGroup]="formGroup">
      <input aocUiInputText aocUiFormField="Legal name" [formControlName]="LegalDataTemplateClass.field.LEGAL_NAME">
      <input [span]="6" aocUiInputText aocUiFormField="Document number" [formControlName]="LegalDataTemplateClass.field.DOCUMENT_NUMBER">
      <app-identity-document-type-autocomplete
        [span]="6"
        aocUiFormField="Identity document type"
        [formControlName]="LegalDataTemplateClass.entity.IDENTITY_DOCUMENT_TYPE"
      ></app-identity-document-type-autocomplete>
    </aoc-ui-form-row>
  </ng-template>
</aoc-subform>

The LegalDataTemplate model is an AocEmbeddedModel which is just a normal model with some simplifications (take a look at mikro-orm.

protected LegalDataTemplateClass = LegalDataTemplate;

Api #

Full definition in the API.

aocformcontroller #

In forms, always provide AocFormController. This class functions as a tool to control the lifecycle of forms and allows us to hook into hooks, among other functionalities.

Additionally, it can also be injected into components within the same hierarchy as our parent form.

providers: [ AocFormController ]

For example, we reload the browser when we have clicked on the Save button of a form and the model has been saved successfully. The model is received as a parameter.

this.aocFormController.addAfterSaveAction(_ => {
  window.location.reload();
});

Api #

Full definition in the API.

Please note, browse Issues and Discussions in Github for more information

© 2024 Atlantis of Code. All rights reserved.
All trademarks are the property of their respective owners.