List builder

Introduction

With the list builder you can create list tables without writing code.

Features

title

Define the title of the list. (Translation key)

Supported ✓ \ x
UI

Example

return [
    'title' => 'order.title',
...

source

Define the source of the list. For now we only support source type url. In the near feature we will add more source types.

Options

  • endpoint; an API endpoint. (note without ‘/api’ the frontend will add this)
Supported ✓ \ x
UI
return [
    'source' => [
        'endpoint' => '/order',
    ],
...

Default sorting

Supported ✓ \ x
UI X

Example

return [
    'source' => [
        'endpoint' => '/order',
        'options' => [
            'sort' => [
                [
                    'field' => 'number',
                    'direction' => 'asc',
                ],
            ],
        ],
    ],
...

Default filters

Supported ✓ \ x
UI X
return [
    'source' => [
        'endpoint' => '/order',
        'options' => [
            'filter' => [
                'type' => [
                    'operator' => 'equals',
                    'value' => 'form',
                ],
            ],
        ],
    ],
...

pagination

Pagination types: infinite scroll or paginated.

Supported ✓ \ x
UI

Example

return [
       'pagination' => 'infinite',
...

permissions

The current user must have at least one of those permissions to view the list. When you set the value null, Everyone can see it.

Supported ✓ \ x
UI

Example

return [
    'permissions' => ['user.show'],
...

columns

Set the columns of the list.

Options

  • Column; the name of the column
  • Type;
    • StringType
    • ArrayType
    • DateType
    • BooleanType
    • IntegerType
    • MoneyType
    • TemplateType
  • Title (optional) (translation key); Title of the column for FE to render the header or whatever.
  • Sortable – is the column sortable
  • Options
    • css_class – add a css class
    • disable_row_click
Supported ✓ \ x
UI
Customizations Add custom column type
Alter column

Example

return [
    'columns' => [
        [
            'column' => 'order_number',
            'field' => 'number',
            'type' => IntegerType::class,
            'title' => 'order_nr.label',
        ],
        [
            'column' => 'order_value',
            'field' => 'sum_value',
            'type' => MoneyType::class,
            'title' => 'order.value.label',
            'options => [
                'css_class' => 'example_css_class',
                'disable_row_click' => true,
              ],
        ],
        [
            'column' => 'username',
            'field' => 'user.name',
            'type' => StringType::class,
            'title' => 'user_name.label',
        ],
...

columns - date

By a date column you can also define a date format

Supported ✓ \ x
UI X

Example

return [
    'columns' => [
        [
            'column' => 'external_delivery_at',
            'type' => [
                'class' => DateType::class,
                'options' => [
                    'format' => 'd-m-Y',
                ],
            ],
        ],
...

columns - base data

You can show baseData with a custom widget

Supported ✓ \ x
UI X

Example

return [
    'columns' => [
        [
            'column' => 'order_status',
            'field' => 'order_status_base_data_value_id',
            'type' => TemplateType::class,
            'title' => 'status.label',
            'widget' => [
                'name' => 'baseData',
            ],
        ],
...

actions

Define the actions of the list. For example what need to happen on click or on edit.

Options

  • Key; the key of the action
  • Name; the name of the action (translation key)
  • Permission; the permission you need to execute this action
  • icon; the icon for showing in the list
  • type; (dot_menu, on_row_click, on_column_click, bulk_action, header_button)
  • action; the action
    • type; (navigate, open_popup, open_sidebar, backend, backend_confirmation, drop (Drag and drop action doesn’t work on responsive screens)
    • route: the frontend route

The backend action will send the selected row(s) to the service: array with all row attributes)

Supported ✓ \ x
UI
Customizations Add custom action type
React after action

Example

return [
    'actions' => [
        [
            'key' => 'edit',
            'name' => 'button.edit',
            'permissions' => ['user.show'],
            'icon' => 'far fa-edit',
            'type' => 'dot_menu',
            'action' => [
                'type' => 'navigate',
                'route' => '/maker/user/detail/{{username}}',
            ],
        ],
        [
            'key' => 'edit',
            'name' => 'button.edit',
            'permissions' => ['user.show'],
            'icon' => 'far fa-edit',
            'type' => 'bulk_action',
            'action' => [
                'type' => 'backend',
                'class' => 'class',
                'method' => 'method',
            ],
        ],
        [
            'key' => 'custom_alert',
            'name' => 'button.show_alert',
            'permissions' => null,
            'icon' => 'fas fa-exclamation-triangle',
            'action' => [
                'type' => 'showAlert',
            ],
        ],
...

filters

The filters above the list

  • global search – true/false if you want to enable global search
  • reset_button – if you want to render the reset button
  • refresh_button – if you want to render the refresh data button
  • remember filters – if you want that filter input is remembered in local storage and prefilled at the next visit.
  • fields – the filters

The default is false

Supported ✓ \ x
UI

Example

return [
    'filters' => [
        'global_search' => true,
        'reset_button' => [
            'title' => 'reset.button',
            'icon' => 'fas fa-times-circle',
        ],
        'refresh_button' => [
            'title' => 'refresh.button',
            'icon' => 'fas fa-sync',
        ],
        'fields' => [
            [
                'name' => 'number',
                'operator' => Operator::EQUALS,
                'type' => TextInputType::class,
                'label' => 'list_orders.filter_order_number.label',
                'placeholder' => 'list_orders.filter_order_number.placeholder',
                'icon' => 'fas fa-plus',
            ],
...

Autocomplete filters

Add an autocomplete from backend filter

Supported ✓ \ x
UI X

Example

   'fields' =>[
            [
                'name' => 'status_id',
                'operator' => Operator::IN,
                'type' => AutoCompleteInputType::class,
                'label' => 'list_orders.filter_status_id.label',
                'placeholder' => 'list_orders.filter_status_id.placeholder',
                'icon' => 'fas fa-plus',

                // ActionInterface
                'options' => [
                    // 'endpoint' => '', // optional: defaults to /api/builder/list/{config-key}/input/{input-name}
                    // 'options' => [],  // optional: defaults to []

                    // CallableInterface
                    'list' => [
                        'class' => BaseDataListService::class,
                        'method' => 'list',
                        'options' => [
                            'meta' => [
                                'key' => 'order_status',
                            ],
                        ],
                    ],
                ],
            ],

Resettable filters

Add a reset filter option.

Supported ✓ \ x
UI

Example

   'fields' =>[
            [
                'name' => 'status_id',
                'operator' => Operator::IN,
                'type' => AutoCompleteInputType::class,
                'label' => 'list_orders.filter_status_id.label',
                'placeholder' => 'list_orders.filter_status_id.placeholder',
                'icon' => 'fas fa-plus',
                'resettable' => true,
                'reset_filter_icon' => 'fas fa-trash',
            ],

Context

It is possible to add context to both the list and form renderer. Context is additional information that is needed when storing or retrieving the form.

Supported ✓ \ x
UI X
Configuration X
Customizations Add context

Example

   'fields' =>[
            [
                'name' => 'status_id',
                'operator' => Operator::IN,
                'type' => AutoCompleteInputType::class,
                'label' => 'list_orders.filter_status_id.label',
                'placeholder' => 'list_orders.filter_status_id.placeholder',
                'icon' => 'fas fa-plus',

                // ActionInterface
                'options' => [
                    // 'endpoint' => '', // optional: defaults to /api/builder/list/{config-key}/input/{input-name}
                    // 'options' => [],  // optional: defaults to []

                    // CallableInterface
                    'list' => [
                        'class' => BaseDataListService::class,
                        'method' => 'list',
                        'options' => [
                            'meta' => [
                                'key' => 'order_status',
                            ],
                        ],
                    ],
                ],
            ],

Sticky header

Is the header sticky or not.

Supported ✓ \ x
UI x

Example

return [
    'title' => 'order.title',
    'sticky_header' => true,
    ...
]

Example configuration

path: builder/configurations/list/orders.php


return [
    'title' => 'order.title',
    'source' => [
        'endpoint' => '/order',
    ],
    // The current user must have at least one those permissions to view the list
    'permissions' => ['user.show'],
    'columns' => [
        [
            'column' => 'id',            // Required: Name of column
            'type' => StringType::class, // Required: type to transform data BE and give FE a string as referral to what to render
            'field' => 'id',             // Optional: Property name of full relation.property path for data retrieval
            'title' => '#',              // Optional: Title of the column for FE to render the header or whatever.
        ],
        [
            'column' => 'just_some_fe_column',
            'type' => StringType::class,
            'title' => 'test',
        ],
        [
            'column' => 'order_number',
            'field' => 'number',
            'type' => IntegerType::class,
            'title' => 'order_nr.label',
        ],
        [
            'column' => 'order_value',
            'field' => 'sum_value',
            'type' => MoneyType::class,
            'title' => 'order.value',
        ],
        [
            'column' => 'username',
            'field' => 'user.name',
            'type' => StringType::class,
            'title' => 'user_name.label',
        ],
        [
            'column' => 'created_at',
            'field' => 'created_at',
            'type' => [
                'class' => DateType::class,
                'options' => [
                    'format' => 'd-m-Y',
                ],
            ],
            'title' => 'created_at.label',
        ],
        [
            'column' => 'status_label',
            'field' => 'status.value',
            'type' => StringType::class,
            'title' => 'status.label',
        ],
        [
            'column' => 'image',
            'field' => 'image_url',
            'type' => TemplateType::class,
            'title' => 'status.label',
            'widget' => [
                'name' => 'image',
                'options' => [
                    'shape' => 'circle',
                ],
            ],
        ],
    ],
    'actions' => [
        [
            'key' => 'click',
            'name' => 'button.view',
            'permissions' => null,
            'icon' => 'fas fa-info-circle',
            'action' => [
                'type' => 'navigate',
                'route' => '/maker/user/view/{{username}}',
            ],
        ],
        [
            'key' => 'edit',
            'name' => 'button.edit',
            'permissions' => ['user.show'],
            'icon' => 'far fa-edit',
            'action' => [
                'type' => 'navigate',
                'route' => '/maker/user/detail/{{username}}',
            ],
        ],
        [
            'key' => 'custom_alert',
            'name' => 'Show Alert',
            'permissions' => null,
            'icon' => 'fas fa-exclamation-triangle',
            'action' => [
                'type' => 'showAlert',
            ],
        ],
    ],
];

Customizations

What customization you can do:

  • Custom widget
  • Custom source
  • Custom columnType
  • Context
  • Custom actions

Create a custom widget

Generate component

ng g c my-custom-widget

Extend base class

export class MyCustomWidgetComponent extends ListBuilderWidgetBase {
  @Input() customProperty: string;
}

Register new widget

To register custom widget in list-builder functionality it’s enough to provide new custom component in module’ s forRoot

@NgModule({
  imports: [
    CapturumListRendererModule.forRoot({
      widgets: {
        'custom-widget': MyCustomWidgetComponent
      }
    })
  ]
})

Config

return [
...
            'column' => 'image',
            'field' => 'image_url',
            'type' => TemplateType::class,
            'title' => 'Status',
            'widget' => [
                'name' => 'custom-widget',
                'options' => [
                     'customProperty' => 'dynamic-data'
                ],
            ],
        ],
...
}

Create a custom source

Make sure the class implements the SourceInterface.

Create a custom columnType on the backend

Make sure the class implements the ColumnTypeInterface or extend `AbstractColumnType.

AbstractColumnType

getType() -> the string type send to the frontend to indentify this column
cast($originalValue, $model, $key, $fieldMeta = []) -> cast/change the output

Context

It is possible to add context to both the list and form renderer. Context is additional information that is needed when storing or retrieving the form. To add this context you can set the context input property on the form- and list-renderer.

<cpb-list-renderer key="my_list" [context]="{product_id: '123'}"></cpb-list-renderer>

With every HTTP request that involves the specified list renderer it will append this context in the query params of the url. for the example above it would for example be: http://builders.test/api/my_list/submit?_context[product_id]=123

Retrieve context

If you wish to retrieve the context of a speicific form or list you can use the CapturumBuildersContextService:

import { CapturumBuildersContextService } from '@capturum/builders/core';

class myService {
  constructor(private contextService: CapturumBuildersContextService) {
    const context = this.contextService.getContextByKey('my_list_key');
  }
}

Backend context

ContextManager

find($key) -> find context by key
get() -> get all contexts
add($key, $value)
has($key) -> check if context has a context with a key

Postman

api/builder/source/list_planned_batches?_context[secret_key]=2c993838-3be1-11ec-ba1b

Example ContextManager

ContectManager::find('secret_key') -> 2c993838-3be1-11ec-ba1b

Custom Actions

The @capturum/builders package provides a way to execute actions. These actions are used at the list-renderer and the form-renderer. When you press a row-action it executes an action based on the given configuration. There are a few built in actions available in the @capturum/builders package. These actions are as followed:

  • navigate – With this action the action will navigate to the given route.
  • openDialog – With this action a dialog will be opened with the given component rendered inside of it.
  • openSidebar – With this action a sidebar will be opened with the given component rendered inside of it.

It is also possible to add your own custom actions. This can be done in the forRoot config of the CapturumBuilderCoreModule.

import { CapturumBuilderCoreModule } from '@capturum/builders/core';
@NgModule({
  imports: [
    CapturumBuilderCoreModule.forRoot({
      actions: {
        'myAction': MyAction
      }
    })
  ]
})
class AppModule {}

The actions provided in the forRoot config have to implement the ActionProvider interface. An example of a custom action is as followed:

import { ActionProvider } from '@capturum/builders/core';

@Injectable({ provideIn: 'root' })
class MyActionClass implements ActionProvider {
  public execute(options: any, item: any): void {
    // item contains the context of the action. In the case of a row-action it would be the selected row
    // options contain all the properties sent by the configuration.
    // Perform some magic in here
  }
}

openSidebar/openDialog actions

With the openSidebar and openDialog actions you can open a dialog or sidebar with the specified component rendered inside of it. To make this work the @capturum/builders package has to know what component to render. Per action and configuration you have to specify which component to render inside the sidebar or dialog. This is done in the forRoot config of the CapturumBuilderCoreModule. The naming of the component key should be as followed: <>_<>. So if the configuration key would be list_orders and the action key would be show_details then the component key should be: list_orders_show_details.

In the forRoot config of the CapturumBuilderCoreModule you can specify what name belongs to which component:

import { CapturumBuilderCoreModule } from '@capturum/builders/core';
@NgModule({
  imports: [
    CapturumBuilderCoreModule.forRoot({
      components: {
        'list_orders_show_details': OrderDetailsComponent
      }
    })
  ]
})
class AppModule {}

With this configuration form the backend it will render the OrderDetailsComponent inside of the sidebar.

Action execution messages

Whenever an action is executed a broadcast event is triggered. You can subscribe to this broadcast by calling the getActionExecutions method of the CapturumBuilderActionService. This will return an observable on which you can subscribe to get notified whenever an action is executed. The message will contain the action, the item and the response which was returned from the action.

So if you want to have the response in this message you have to make sure to return a value or observable in the execute function.

Additional features

Pass filters in url queryParams

It is possible to add filters in the url queryParams. When the list-renderer sees these filters in the url it will apply them on the list-renderer. This can be done by doing the following:

public navToPage(): void {
    this.router.navigate(
        ['my-route'],
        { queryParams: ListRendererUtils.generateQueryUrlFilters({myFilter: 'MyValue', myOtherFilter': ['item1', 'item2']'})}
    )
}

This will add the “myFilter” and “myOtherFilter” filters to the url and the list-renderer will know how to apply these filters.

Retrieve active filters

You can retrieve the active filters of a list renderer by calling the ListRendererFiltersService. This service contains a method to get the active filters: getActivefilters It takes the list key as a parameter and it will return an object with all the active filters.

import { ListRendererFiltersService } from '@capturum/builders/list-renderer';

constructor(private listRendererFiltersService: ListRendererFiltersService) {
  const activeFilters = this.listRendererFiltersService.getActiveFilters('list_key');
}

Reset filters

You can reset the filters of the list-renderer from outside the list-renderer by calling the resetFilters function on the ListRenderer. You can simply create a ViewChild property of the listRenderer and call the resetFilters method.

import { CapturumListRendererComponent } from '@capturum/builders/list-renderer';

class AppComponent {
  @ViewChild(LisCapturumListRendererComponent)
    public listRenderer: CapturumListRendererComponent;

  public ngOnInit(): void {
    this.listRenderer.resetFilters();
  }
}

Refresh list

You can refresh the list-renderer by calling the refresh method of the list-renderer. You can simply create a ViewChild property of the listRenderer and call the resetFilters method.

import { CapturumListRendererComponent } from '@capturum/builders/list-renderer';

class AppComponent {
  @ViewChild(LisCapturumListRendererComponent)
    public listRenderer: CapturumListRendererComponent;

  public ngOnInit(): void {
    this.listRenderer.refresh();
  }
}

Refresh table data

You can refresh the list-renderer table by calling the refreshTable method from listRendererService service.

Bulk Actions

You can add some extra content/data to the bulk actions bar, next to ‘amount of selected rows’. To do this you need to use bulkActions as template name. The outlet context will be the selected items.

<cpb-list-renderer key="123">
  <ng-template capTemplate="bulkActions" let-items="items">
    // do something with selected items
  </ng-template>
</cpb-list-renderer>

Conditional row actions

It is possible to conditionally show/hide certain three-dot-menu actions based on the row data. This can be achived by setting a hiddenExpression in the FormRendererConfig. This is done as followed:

    CapturumListRendererModule.forRoot({
      list_selection: { // The key of the list configuration
        actions: {
          delete: { // The key of the action
            hiddenExpression: (row: any) => { // row is the row in the table
              return row.name === 'test'; // condition which should return true/false;
            }
          }
        }
      }
    }),

In the example above the delete action will be hidden whenever the name property of the row si equal to “test”.

Conditional row styleClass

It is possible to conditionally add a styleClass to each row in the list based on the item in the list. This can be achieved by setting the styleClassExpression in the ListRendererModule forRoot config. This can be done as followed:

    CapturumListRendererModule.forRoot({
      list_selection: { // Key of the list
        styleClassExpression: (item) => { // item contains the data of the row
          return item.name === 'test' ? 'bg-red' : null;
        }
      }
    })

In the example above it shows that for the list with list key “list_selection” it will apply the “bg-red” class to each table row if the item in the row has a name property equal to “test”