UNPKG

grcm-components

Version:

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

396 lines (318 loc) 11 kB
# grcm-components > Crea formularios dinámicos en Angular de forma rápida, limpia y reutilizable. <br> --- ## ✨ ¿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`. <br> --- ## 📦 Instalación ```bash npm i grcm-components ``` <br> --- ## ⚙️ 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. <br> --- ## 🚀 Ejemplo básico ```ts 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 }) } } ``` <br> --- ## 🔁 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`: ```html <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: ```html <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: ```ts 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 } } ``` <br> --- ## 🧩 Templates personalizados Puedes insertar templates personalizados antes o después del formulario completo o de campos individuales: ```html <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> ``` <br> --- ## 🧱 Crear tu propio `form-field` ```ts // 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. <br> --- ## ✏️ Crear tu propio componente de input ```ts 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; } } ``` <br> --- ## 🧪 Ejemplo final usando `CustomFormFieldComponent` y `CustomInputComponent` ```ts 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 }) } } ``` <br> --- ## 🧠 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.