grcm-components
TypeScript icon, indicating that this package has built-in type declarations

0.0.8 • Public • Published

grcm-components

⚡ Crea formularios dinámicos en Angular de forma rápida, limpia y reutilizable.



✨ ¿Por qué usar este paquete?

  • ¿Cansado de escribir formularios con FormBuilder y FormGroup una y otra vez?
  • ¿Cansado de escribir en el template todos los campos, clases y estructura cada vez que necesitas un formulario?
  • ¿Leíste esto con voz de anuncio? 😉

Con grcm-components, puedes definir tus formularios dinámicamente desde el archivo .ts, usando un solo componente de formulario configurable. Además, soporta cualquier componente personalizado que implemente ControlValueAccessor.



📦 Instalación

npm i grcm-components


⚙️ Componentes disponibles

  • lib-grcm-form: Componente principal para construir formularios dinámicos.
  • lib-grcm-input: Input simple ya listo para usarse.
  • lib-grcm-button: Botón de ejemplo.
  • lib-grcm-form-field / lib-grcm-plain-form-field: Ejemplos de 'Wrappers' para campos de formulario.
  • lib-grcm-example-login: Un formulario de un login simple de ejemplo.

💡 Nota
Los componentes de ejemplo incluidos usan estilos de Bootstrap.
Sin embargo, no necesitas Bootstrap si usas tus propios form-field y componentes de control personalizados.



🚀 Ejemplo básico

import { Component } from '@angular/core';
import { Validators } from '@angular/forms';
import {
  GrcmFormComponent,
  GrcmInputComponent,
  GrcmFormFieldComponent,
  GrcmButtonComponent,
  GrcmDataForm,
  ResponseGrcmForm,
  createGrcmControl
} from 'grcm-components';

@Component({
  selector: 'app-login-page',
  standalone: true,
  imports: [GrcmFormComponent, GrcmButtonComponent],
  template: `
  <div class="vh-100 d-flex justify-content-center align-items-center">
  <lib-grcm-form
    [data]="dataForm"
    [templates]="{ afterFormTemplate }"
    (formSubmit)="submit($event)">
    <ng-template #afterFormTemplate let-form="form">
      <lib-grcm-button
        [disabled]="form.invalid"
        type="submit"
        class="w-100 mt-2"
        label="Entrar">
      </lib-grcm-button>
    </ng-template>
  </lib-grcm-form>
</div>`,
})
export default class LoginPageComponent {

  protected dataForm: GrcmDataForm = {
    form: {
      id: 'login'
    },
    controls: {
      username: createGrcmControl({
        component: GrcmInputComponent,
        formFieldComponent: GrcmFormFieldComponent,
        value: "",
        validators: [Validators.required, Validators.minLength(3), Validators.maxLength(10)],
        args: {
          control: {
            placeholder: "Escriba su usuario",
            icon: "person-circle",
            autocomplete: false
          },
          formField: {
            label: "Usuario"
          }
        },
        classes: {
          control: "input-group"
        }
      }),
      password: createGrcmControl({
        component: GrcmInputComponent,
        formFieldComponent: GrcmFormFieldComponent,
        value: "",
        validators: [Validators.required, Validators.minLength(6), Validators.maxLength(15)],
        args: {
          control: {
            type: 'password',
            placeholder: "Escriba su contraseña",
            icon: "lock",
            autocomplete: false,
          },
          formField: {
            label: "Contraseña"
          }
        },
        classes: {
          control: "input-group"
        }
      }),
    }
  };

  submit(data: ResponseGrcmForm) {
    const {username, password} = data.form.value;
    console.log({ username, password })
  }
}


🔁 Enviar el formulario con múltiples acciones (submitters)

Si tu formulario tiene más de un botón para distintas acciones:

Usando lib-grcm-button:

<lib-grcm-form [data]="dataForm" [templates]="{ afterFormTemplate }" (formSubmit)="submit($event)">
  <ng-template #afterFormTemplate let-form="form">
    <lib-grcm-button type="submit" submitter="create-room" label="Crear sala"></lib-grcm-button>
    <lib-grcm-button type="submit" submitter="join-room" label="Unirse"></lib-grcm-button>
  </ng-template>
</lib-grcm-form>

Usando botones normales:

<lib-grcm-form [data]="dataForm" [templates]="{afterFormTemplate}" style="width: 100%;"
  (formSubmit)="submit($event)" class="d-block">
  <ng-template #afterFormTemplate let-form="form">
    <div class="d-flex justify-content-between mt-2">
      <button type="submit" data-submitter="create-room">Crear sala</button>
      <button type="submit" data-submitter="join-room">Unirse a la sala</button>
    </div>
  </ng-template>
</lib-grcm-form>

En el componente:

submit(data: ResponseGrcmForm) {
  const action = data.submitter;
  if (action === 'create-room') {
    // lógica para crear sala
  } else if (action === 'join-room') {
    // lógica para unirse
  }
}


🧩 Templates personalizados

Puedes insertar templates personalizados antes o después del formulario completo o de campos individuales:

<lib-grcm-form [data]="dataForm" [templates]="{afterFormTemplate, beforeFormTemplate, before_username, after_username}" (formSubmit)="submit($event)">
  <ng-template #beforeFormTemplate let-form="form">
    Esto aparecerá al principio del formulario.
  </ng-template>
  <ng-template #before_username let-form="form" let-control="control">
    Esto aparecerá antes del campo de username.
  </ng-template>
  <ng-template #after_username let-form="form" let-control="control">
    Esto aparecerá después del campo de username.
  </ng-template>
  <ng-template #afterFormTemplate let-form="form">
    Esto aparecerá al final del formulario.
  </ng-template>
</lib-grcm-form>


🧱 Crear tu propio form-field

// custom-form-field.component.ts
import { CommonModule } from '@angular/common';
import { Component, input, OnDestroy, viewChild, ViewContainerRef } from '@angular/core';
import { AbstractControl } from '@angular/forms';
import { GrcmFormField } from 'grcm-components';

@Component({
  selector: 'custom-form-field',
  imports: [CommonModule],
  template: `
    <label [for]="id()">{{ label() }}</label>

    <ng-container #controlView></ng-container>

    @let errors = control().errors;
    <p style="color: red;">{{errors | json}}</p>
    `
})
export class CustomFormFieldComponent implements GrcmFormField, OnDestroy {

  public controlView = viewChild.required('controlView', { read: ViewContainerRef });
  public control = input.required<AbstractControl>();
  public id = input.required<string>();

  // Aquí puedes añadir las propiedades que necesites:
  public label = input<string>('');

  ngOnDestroy(): void {
    this.controlView().clear();
  }
}

⚠️ Nota

Si modificas un form-field personalizado, es posible que los cambios no se reflejen correctamente con recarga en caliente (HMR).

Para evitar errores de renderizado, puedes optar por una de estas opciones:

  • Reiniciar la aplicación.
  • Ejecutar ng serve --no-hmr para desactivar HMR temporalmente mientras estés modificando tu form-field personalizado.


✏️ Crear tu propio componente de input

import { Component, computed, signal, inject } from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
import { GrcmControlComponent } from 'grcm-components';

@Component({
  selector: 'custom-input',
  standalone: true,
  template: `
  <input
  type="text"
  [attr.id]="id()"
  [attr.disabled]="disabled() ? true : null"
  class="form-control"
  [class.is-valid]="control()?.valid && control()?.touched"
  [class.is-invalid]="control()?.invalid && control()?.touched"
  [value]="value()"
  (input)="change(input.value)"
  (blur)="onBlur()"
  #input/>`
})
export class CustomInputComponent implements ControlValueAccessor {
  private readonly _controlComp = inject(GrcmControlComponent);

  public control = computed(() => this._controlComp.control());
  public id = computed(() => this._controlComp.id());

  private _value = signal<string>('');
  private _disabled = signal<boolean>(false);

  protected value = this._value.asReadonly();
  protected disabled = this._disabled.asReadonly();

  change(value: string) {
    this._onTouched();
    this._onChange(value);
  }

  onBlur() {
    this._onTouched();
  }

  _onChange: Function = () => { };
  _onTouched: Function = () => { };

  writeValue(value: unknown): void {
    if (typeof value === 'string') this._value.set(value);
  }

  setDisabledState(isDisabled: boolean): void {
    this._disabled.set(isDisabled);
  }

  registerOnChange(fn: Function): void {
    this._onChange = fn;
  }

  registerOnTouched(fn: Function): void {
    this._onTouched = fn;
  }
}


🧪 Ejemplo final usando CustomFormFieldComponent y CustomInputComponent

import { Component } from '@angular/core';
import { Validators } from '@angular/forms';
import {
  GrcmFormComponent,
  GrcmButtonComponent,
  GrcmDataForm,
  ResponseGrcmForm,
  createGrcmControl
} from 'grcm-components';
import { CustomFormFieldComponent } from './custom-form-field.component';
import { CustomInputComponent } from './custom-input.component';

@Component({
  selector: 'custom-form-page',
  standalone: true,
  imports: [GrcmFormComponent, GrcmButtonComponent],
  template: `
  <div class="vh-100 d-flex justify-content-center align-items-center">
  <lib-grcm-form [data]="dataForm" (formSubmit)="submit($event)" [templates]="{afterFormTemplate}">
  <ng-template #afterFormTemplate let-form="form">
    <lib-grcm-button type="submit" label="Enviar" class="w-100 mt-2"></lib-grcm-button>
  </ng-template>
</lib-grcm-form>
</div>`,
})
export default class CustomFormPageComponent {

  protected dataForm: GrcmDataForm = {
    form: { id: 'custom-form' },
    controls: {
      myCustomField: createGrcmControl({
        component: CustomInputComponent,
        formFieldComponent: CustomFormFieldComponent,
        value: '',
        validators: [Validators.required],
        args: {
          control: {},
          formField: {
            label: 'Mi campo personalizado'
          }
        }
      })
    }
  };

  submit(data: ResponseGrcmForm) {
    const { myCustomField } = data.form.value;
    console.log({ myCustomField })
  }
}


🧠 Conclusión

grcm-components es una solución flexible y extensible para reducir la repetición en formularios de Angular. Define la estructura en tu .ts, usa tus propios inputs o campos, y gana en mantenibilidad y claridad.

Package Sidebar

Install

npm i grcm-components

Weekly Downloads

4

Version

0.0.8

License

MIT

Unpacked Size

127 kB

Total Files

25

Last publish

Collaborators

  • guillercm