npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

grcm-components

v0.0.8

Published

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

Readme

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.