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

@fmcoldays/post-action

v0.4.24

Published

Ejecuta peticiones HTTP declarativas al hacer click en elementos con data-action-url (confirmación, loading, targets, notificaciones, descarga, redirección).

Readme

@fmcoldays/post-action

Ejecuta peticiones HTTP desde cualquier elemento HTML con data-action="true". Soporta confirmación, loading state, actualización de targets en el DOM, notificaciones, descarga de archivos y redirección. Sin dependencias externas.

npm install @fmcoldays/post-action

Las notificaciones y la confirmación las da @fmcoldays/notify (dependencia interna, se instala sola).

Uso

Importar una vez al arrancar la app; el listener se registra solo:

import '@fmcoldays/post-action'

Control manual (opcional):

import { registerPostAction } from '@fmcoldays/post-action'

const unregister = registerPostAction()
// unregister() para quitar el listener

Funciona en cualquier elemento (<button>, <a>, <form>, <select>, <div>…) mediante event delegation.


Atributos

Requerido

| Atributo | Tipo | Descripción | |---|---|---| | data-action | "true" | Activa todos los listeners en el elemento. Requerido en cualquier elemento que use atributos data-action-*. Sin él, data-action-submit, data-action-bind y cualquier otro atributo data-action-* son ignorados. |

Petición

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | data-action-url | string (URL) | — | URL de la petición. Si se omite: en <form> usa el atributo action; en <a> usa el href. | | data-action-method | string | "POST" | Método HTTP. Si se omite: en <form> usa el atributo method (default GET del browser); en <a> usa GET; en otros elementos usa POST. | | data-action-body | string (JSON) | — | Body como JSON literal. En GET se añade al query string. | | data-action-body-form | string (selector) | — | Selector de un <form> cuyo contenido se envía como FormData (POST) o query string (GET). | | data-action-pick | string (JSON) | — | Mapa { "campo": "#selector" } resuelto al momento del click. GET → query string. POST → form-encoded si algún selector apunta a un <select multiple>, JSON en caso contrario. | | data-action-csrf | "true" | — | Incluye el token CSRF en el header RequestVerificationToken. |

Forms: <form data-action="true"> intercepta el submit y envía los campos como FormData. El token CSRF de ASP.NET (campo oculto __RequestVerificationToken) va incluido automáticamente.

Confirmación

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | data-action-confirm | "true" | — | Muestra diálogo de confirmación antes de ejecutar. | | data-action-confirm-title | string | "¿Estás seguro?" | Título del diálogo. | | data-action-confirm-description | string | "Esta acción no se puede deshacer." | Mensaje del diálogo. |

Select autónomo (select[data-action="true"])

Un <select> con data-action="true" y data-action-url ejecuta una petición GET cada vez que su valor cambia, añadiendo automáticamente name=value a la URL. No necesita estar dentro de un <form>.

| Atributo | Default | Notas | |---|---|---| | data-action-method | "GET" | Diferente al default POST de otros elementos. | | data-action-dismiss | false | Nunca cierra el modal padre, salvo que se ponga explícitamente. |

<!-- Al cambiar empresa → GET /ComandoSensor/VehiculosOptions?id_empresa=5 → llena #sel-vehiculo -->
<select name="id_empresa"
        data-action="true"
        data-action-url="/ComandoSensor/VehiculosOptions"
        data-action-target="#sel-vehiculo"
        data-action-silent="true">
  <option value="1">Empresa A</option>
  <option value="2">Empresa B</option>
</select>

Select2: si el <select> está inicializado con Select2, el bridge jQuery (select2:select / select2:unselect) actúa automáticamente cuando jQuery está disponible en window.$. No requiere configuración adicional ni jQuery como dependencia del paquete.


Submit automático (data-action-submit)

Dispara requestSubmit() en un form cuando el elemento cambia o recibe input. Útil para filtros que se envían solos sin botón.

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | data-action-submit | string (selector) o vacío | — | Selector del form a enviar. Si está vacío usa el form padre. | | data-action-submit-delay | number (ms) | 400 para texto, 0 para el resto | Debounce antes de disparar el submit. Aplica a cualquier tipo de elemento. |

<!-- Select dentro del form: dispara submit al cambiar -->
<select name="estado" data-action-submit>…</select>

<!-- Input de texto fuera del form: debounce 400ms por defecto -->
<input name="buscar" data-action-submit="#mi-form" />

<!-- Select fuera del form con delay personalizado -->
<select name="pagSize" data-action-submit="#mi-form" data-action-submit-delay="300">…</select>

Para inputs con min/max, el valor se clampea automáticamente antes de enviar y mientras el usuario escribe.

Clonar valor en un form (data-action-bind)

Crea un <input type="hidden"> dentro del form con el valor del elemento original. Se mantiene sincronizado en tiempo real. Útil cuando el control está fuera del <form>.

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | data-action-bind | string (selector) | — | Selector del form donde se inyecta el clon. | | data-action-bind-visible | "true" | — | Hace visible el clon (útil para debug). |

<!-- Select fuera del form: su valor viaja al form como hidden input -->
<select name="pageSize"
        data-action-bind="#filter-form"
        data-action-submit="#filter-form">…</select>

<!-- Input de texto fuera del form -->
<input name="pageNumber" type="text"
       min="1" max="@pgTotal"
       data-action-bind="#filter-form"
       data-action-submit="#filter-form" />

Si el form es reemplazado por AJAX (data-action-target), el clon se recrea automáticamente.

Limpiar form antes de la petición

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | data-action-clear-form | string (selector) | — | Llama a form.reset() en el form indicado antes de hacer la petición (después de la confirmación). Útil en botones que limpian filtros: primero resetea el form y luego hace la petición a la URL limpia. |

<button type="button"
        data-action="true"
        data-action-url="/Empleados"
        data-action-target="#ajaxresult"
        data-action-skeleton="#sk-table"
        data-action-clear-form="#filter-form">
  <span>filter_alt_off</span>
  Limpiar
</button>

Loading

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | data-action-disable | "true" | — | Deshabilita el elemento (o el botón de submit) durante la petición. | | data-action-loading-class | string | — | Clases CSS a añadir al trigger durante la petición (ej. "loading loading-spinner"). |

Resultado

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | data-action-target | string (selector) | — | Selector del elemento donde se inyecta la respuesta HTML. | | data-action-target-prop | "html" \| "text" \| "value" | "html" | Propiedad del target a actualizar. | | data-action-download | "true" | — | Trata la respuesta como archivo descargable. El nombre se toma del header Content-Disposition. Silencioso por defecto. | | data-action-silent | "true" | "false" | — | "true" suprime la notificación de éxito. "false" la fuerza aunque haya data-action-target. Sin poner: notifica solo si no hay target. Los errores se muestran siempre. | | data-action-success-msg | string | "La operación se completó exitosamente." | Mensaje de éxito. | | data-action-error-msg | string | "Ocurrió un error al realizar la operación." | Mensaje de error. |

Post-éxito

| Atributo | Tipo | Descripción | |---|---|---| | data-action-reload | "true" | Recarga la página tras éxito. | | data-action-redirect | string (URL) | Redirige a esta URL tras éxito. | | data-action-then | string (selector) | Hace click en este elemento tras éxito. |


Ejemplos

DELETE con confirmación y recarga

<button type="button"
        data-action="true"
        data-action-url="/api/items/42"
        data-action-method="DELETE"
        data-action-confirm="true"
        data-action-confirm-title="¿Eliminar item?"
        data-action-confirm-description="Esta acción no se puede deshacer."
        data-action-reload="true">
  Eliminar
</button>

Form — guardar y refrescar tabla

<!-- action y method del form se usan como fallback, no hace falta data-action-url -->
<form action="/Clientes/Crear"
      method="post"
      data-action="true"
      data-action-disable="true"
      data-action-then="#filtro-form">

  <input name="Nombre" />
  <input name="Email" />
  <button type="submit">Guardar</button>
</form>

Form — confirmar antes de enviar

<form action="/Pedidos/Confirmar"
      method="post"
      data-action="true"
      data-action-confirm="true"
      data-action-confirm-title="Confirmar pedido"
      data-action-confirm-description="¿Enviar este pedido? No se puede modificar después."
      data-action-redirect="/Pedidos">

  <!-- campos -->
  <button type="submit">Confirmar</button>
</form>

Enlace que inyecta HTML (GET desde href)

<!-- href se usa como URL y GET como method por defecto -->
<a href="/Clientes/42/Detalle"
   data-action="true"
   data-action-target="#panel-detalle"
   data-action-silent="true">
  Ver detalle
</a>

GET que inyecta HTML en un target

<button type="button"
        data-action="true"
        data-action-url="/api/preview/42"
        data-action-method="GET"
        data-action-target="#preview-container"
        data-action-silent="true">
  Ver preview
</button>

GET con parámetros dinámicos de inputs

<button type="button"
        data-action="true"
        data-action-url="/Reportes/Filtrar"
        data-action-method="GET"
        data-action-pick='{"fecha": "#fecha", "id": "#id_caja"}'
        data-action-target="#resultado"
        data-action-silent="true">
  Filtrar
</button>

POST con <select multiple> (multi-select)

Cuando alguno de los selectores en data-action-pick apunta a un <select multiple>, el body cambia automáticamente a application/x-www-form-urlencoded con claves repetidas, compatible con el model binding de ASP.NET MVC (List<string>, IEnumerable<int>, etc.).

<!-- Guarda asignaciones de un vehículo (operadores, tarjetas, estaciones son multi-select) -->
<button type="button"
        data-action="true"
        data-action-url="/Vehiculos/SaveDatosVehiculo"
        data-action-method="POST"
        data-action-pick='{"id_vehiculo":"#id_v_42","fld_litrosPermitidos":"#litros-42","id_ruta":"#ruta-42","fld_tarjetas":"#tarjetas-42","fld_empleados":"#operadores-42","fld_estaciones":"#estaciones-42"}'>
  Guardar
</button>
// ASP.NET MVC — bindea correctamente sin [FromBody]
[HttpPost]
public ActionResult SaveDatosVehiculo(
    int id_vehiculo,
    decimal fld_litrosPermitidos,
    List<string> id_ruta,
    List<string> fld_tarjetas,
    List<string> fld_empleados,
    List<string> fld_estaciones) { … }

Descargar archivo Excel

<button type="button"
        data-action="true"
        data-action-url="/SensorData/GenerarReporte"
        data-action-method="GET"
        data-action-pick='{"id": "#id_caja", "fecha": "#fecha"}'
        data-action-download="true">
  Descargar Excel
</button>

Toggle silencioso que actualiza el DOM

<button type="button"
        data-action="true"
        data-action-url="/api/toggle/42"
        data-action-target="#status-badge"
        data-action-silent="true"
        data-action-loading-class="loading loading-spinner">
  Activar
</button>

Respuesta JSON esperada del backend

El flujo de éxito o error se determina en este orden:

  1. HTTP status — 4xx/5xx siempre es error.
  2. Campo success booleano (cualquier capitalización) — true = éxito, false = error.
  3. successRules / errorRules configuradas globalmente — primera que aplique gana.
  4. Sin ninguno de los anteriores — se confía en el HTTP status.

El único campo de mensaje reconocido es message (en cualquier capitalización: message, Message, MESSAGE…).

{ "message": "Guardado correctamente." }
{ "success": true,  "message": "Guardado correctamente." }
{ "success": false, "message": "El correo ya existe." }

En ASP.NET MVC:

// Éxito HTTP
return Ok(new { message = "Guardado correctamente." });

// Error HTTP
return BadRequest(new { message = "El correo ya existe." });

// Éxito/error en el body (HTTP 200 siempre)
return Json(new { success = true,  message = "Guardado correctamente." });
return Json(new { success = false, message = "El correo ya existe." });

Configuración global de reglas

Si tu backend usa una convención propia (ej. id == 200 como éxito), configúralo una vez al arrancar la app:

import { PostAction, fieldEquals, fieldNotEquals } from '@fmcoldays/post-action'

PostAction.configure({
  successRules: [fieldEquals('id', 200)],
  errorRules:   [fieldNotEquals('id', 200)],
})

Con @fmcoldays/all:

import { configure, fieldEquals, fieldNotEquals } from '@fmcoldays/all'

configure({
  postAction: {
    successRules: [fieldEquals('id', 200)],
    errorRules:   [fieldNotEquals('id', 200)],
  },
})

| Helper | Descripción | |---|---| | fieldEquals(field, value) | Éxito/error si data[field] === value. | | fieldNotEquals(field, value) | Éxito/error si data[field] !== value. |

También puedes pasar funciones custom:

PostAction.configure({
  successRules: [
    (data) => 'code' in data ? data.code === 1 : undefined
  ],
})

Cada función devuelve true, false, o undefined (no aplica, pasa a la siguiente regla).


Notas

  • Para <input type="checkbox"> o <input type="radio">: el estado se togglea tras el éxito.
  • data-action-reload y data-action-redirect son mutuamente excluyentes; redirect tiene prioridad.
  • data-action-download y data-action-target son mutuamente excluyentes; con download no se inyecta HTML.
  • data-action-clear-form aplica solo a elementos que disparan la petición directamente (data-action="true" + data-action-url). No aplica a data-action-submit ni data-action-bind.

API programática

| Export | Descripción | |---|---| | registerPostAction(root?) | Registra los listeners de click, submit, change e input. Devuelve una función para desregistrar. | | handlePostActionClick(e) | Handler de click crudo. | | handlePostActionSubmit(e) | Handler de submit crudo. | | handlePostActionSelectChange(select) | Handler de change para select[data-action="true"]. Usado internamente; útil si integras tu propio bridge de eventos. | | handleActionSubmitTrigger(e) | Handler de data-action-submit (change/input). | | bindElements(root?) | Procesa data-action-bind en el root dado. Idempotente. | | executeRequest(opts) | Ejecuta la petición HTTP a partir de un ActionOptions. | | parseOptions(el) | Lee los data-action-* de un elemento a ActionOptions. | | PostAction.configure(opts) | Configura reglas globales de éxito/error. | | fieldEquals(field, value) | Helper — regla que evalúa data[field] === value. | | fieldNotEquals(field, value) | Helper — regla que evalúa data[field] !== value. | | Tipos | ActionOptions, ActionResult, PostActionConfig, SuccessRule, ErrorRule. |

Estructura

src/
├── index.ts                        # barril + auto-registro
├── register.ts                     # listeners (click, submit, change, input, post-action:updated)
├── handle-post-action-click.ts        # orquestador para elementos clickeables
├── handle-post-action-submit.ts       # orquestador para forms
├── handle-post-action-select-change.ts # orquestador para select[data-action="true"] + bridge Select2
├── handle-action-submit-trigger.ts    # data-action-submit (debounce, clamp min/max)
├── bind-elements.ts                # data-action-bind (clon hidden + re-bind tras AJAX)
├── types/                          # ActionOptions, ActionResult
├── helpers/                        # options, dom, notify, download, server-message
└── services/                       # request.service (fetch + shared.buildHttpPayload)