Blueprint projects
- Frontend
- Blueprint assignments
- Blueprint builders
- Blueprint general
- Blueprint how to
- Blueprint tools
- Backend
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”