Overview

Custom Actions

The framework lets you add custom actions on top of a <wuic-list-grid>. There are two distinct types, with different metadata table and callback signature:

  • Table action (bulk action): a button in the toolbar above the grid, operating on a multiple selection or the whole view.
  • Row action: a button on each row, operating on the single record.

> The distinction matters: _mtdt__cstom__actions__tabelle contains only table actions. For a row action you instead create a virtual column in _metadati__colonne. There is no scope field in _mtdt__cstom__actions__tabelle: an action placed there is always a table action, even if the callback uses record.

Table action (toolbar / bulk)

It is registered in _mtdt__cstom__actions__tabelle. The callback has this signature:

Snippet 1js
function (datasource, metaInfo, record, event, wtoolbox) {
  // datasource : the grid's DataSourceComponent
  // metaInfo   : table/column metadata
  // record     : current / selected records
  // event      : UI event
  // wtoolbox   : framework services (notifications, dialog, ...)
}
  • To enable multiple selection on the grid set mdmultipleselection=1 on the table metadata.
  • To refresh the UI after the action use datasource.fetchData() (NOT refresh()).
  • For a confirmation toast use wtoolbox.messageNotificationService.add().
Snippet 2js
async function (datasource, metaInfo, record, event, wtoolbox) {
  const ids = (datasource.resultInfo?.selected || []).map(r => r.id.value);
  await fetch('/api/Custom/bulkApprove', { method: 'POST', body: JSON.stringify(ids) });
  await datasource.fetchData();
  wtoolbox.messageNotificationService.add({ severity: 'success', summary: 'Approvati', detail: ids.length + ' record' });
}

Row action (per-row button)

It is created as a virtual column in _metadati__colonne:

  • mc_ui_column_type = 'button'
  • mc_voa_class = 6 (button-column class)
  • callback in the mcbuttonaction field (text)

The callback receives the row's record and is therefore suited to single-record actions (printing a single row, opening a one-click dialog, changing the current record's state).

Snippet 3SQL
-- example: "Stampa" button on each row of fatture_inviate
INSERT INTO _metadati__colonne (md_id, mc_nome_colonna, mc_ui_column_type, mc_voa_class, mcbuttonaction, mc_ordine)
VALUES (@md_id, 'print_action', 'button', 6, '<callback JS>', 100);

Comparison table

Table actionRow action
Metadata table_mtdt__cstom__actions__tabelle_metadati__colonne (virtual column)
UI positiontoolbar above the gridbutton on each row
Scopemultiple selection / viewsingle record
Callback(datasource, metaInfo, record, event, wtoolbox)callback in mcbuttonaction, receives the record
Multi-selectmdmultipleselection=1n/a
UI refreshdatasource.fetchData()datasource.fetchData()

See also

<!-- incode-injection -->

In-code injection (this component only)

Table actions can also be injected directly in the component `.ts`, with no SQL patch. Key difference: the SQL patch on _mtdt__cstom__actions__tabelle makes the action visible in every component using that route (persistent, route-wide); the in-code injection limits it to this component only.

The action is an element of the metaInfo.tableMetadata._Metadati_Custom_Actions_Tabelles array (already initialized to []). Fields of the MetadatiCustomActionTabella class: button_caption (label, required), button_image? (PrimeNG icon), action_callback__fn — the FUNCTION run on click, signature (datasource, metaInfo, record, event, wtoolbox) => void (not a string; the serialized string variant is action_callback), disable_callback__fn? for conditional disabling, ordine?.

Wiring: get the DataSourceComponent via @ViewChild('ds', { static: true }) and push in `ngOnInit` (NOT ngAfterViewInit) when fetchInfo$ publishes the metadata — this way the patch precedes the first render of the child <wuic-list-grid> (which subscribes to fetchInfo$ in its own ngOnInit, after the parent's). Idempotent .some(...) guard because fetchInfo$ re-emits. Refresh the UI with datasource.fetchData() (not refresh()).

Snippet 4ts
import { Component, ViewChild, OnInit } from '@angular/core';
import { DataSourceComponent, ListGridComponent } from 'wuic-framework-lib';

@Component({
  selector: 'app-aziende-grid',
  imports: [DataSourceComponent, ListGridComponent],
  template: `

In-code injection — row action (per-row button)

The row action (a button on each row) is injected in-code as a virtual button column in metaInfo.columnMetadatanot in _Metadati_Custom_Actions_Tabelles (that is toolbar/bulk only).

⚠️ Critical difference from the DB version. The mcbuttonaction string is compiled into mc_button_action__fn by MetadataProviderService only when the metadata is loaded from the DB. A column pushed in ngOnInit is after that load → the string is never compiled, and DataActionButtonComponent (which requires mc_button_action__fn in isMetadataButtonEnabled) leaves the button disabled. So in-code you set the FUNCTION `mc_button_action__fn` directly, not the string.

Fields of the button column (runtime, MetadatiColonna class):

  • mc_ui_column_type: 'button'
  • mc_button_caption — button label (without it → label = mc_nome_colonna, e.g. "rinnova_action" instead of "Rinnova")
  • mc_button_image? — PrimeNG icon (e.g. 'pi pi-refresh')
  • mc_button_action__fn — the FUNCTION run on click, signature (datasource, record, event, field, wtoolbox) => void (NOT the mcbuttonaction/mc_button_action string)
  • mc_button_confirm_message?, mc_button_visibility_condition? — optional

record in the callback is { [col]: BehaviorSubject<any> } → read the values with record['<col>']?.value (the PK with record['<PK>']?.value).

Snippet 5ts
ngOnInit() {
  this.ds.fetchInfo$.subscribe(info => {
    if (!info?.metaInfo) return;
    const cols = info.metaInfo.columnMetadata;
    if (cols.some(c => c.mc_nome_colonna === 'rinnova_action')) return; // idempotent (BehaviorSubject re-emits)
    cols.push({
      mc_nome_colonna: 'rinnova_action',

Versus the DB version (the "Row action" section above): there the callback is the string mcbuttonaction, compiled by the framework at load time. The two paths are NOT interchangeable: DB = `mcbuttonaction` string, in-code = `mc_button_action__fn` function.