This section covers searching and filtering in the va-data-table-server and va-data-iterator-server components used with the va-list component.
The global search filter will be enabled by default. To disable this, use the disableGlobalSearch property. This filter will send the search query to the backend via the key configured in the globalSearchQuery variable, which is q by default. Then, on the backend side for SQL processing, for example, the multi-column LIKE search will be done automatically, thanks to the relevant model Olobase\Mezzio\ColumnFilters class.
<template>
<va-list :fields="fields" :filters="filters">
<va-data-table-server disable-show :disable-actions="false">
</va-data-table-server>
</va-list>
</template>
<script>
export default {
props: ["resource"],
data() {
return {
filters: [],
fields: [
{
source: "roleName",
sortable: true,
},
{
source: "roleKey",
sortable: true,
},
{
source: "roleLevel",
sortable: true,
},
],
};
}
};
</script>
<?php namespace App\Model;
use Exception;
use Olobase\Mezzio\ColumnFiltersInterface;
use Laminas\Db\Sql\Sql;
use Laminas\Db\Sql\Expression;
use Laminas\Paginator\Paginator;
use Laminas\Paginator\Adapter\DbSelect;
use Laminas\Db\Adapter\AdapterInterface;
use Laminas\Cache\Storage\StorageInterface;
use Laminas\Db\TableGateway\TableGatewayInterface;
class RoleModel
{
private $conn;
private $roles;
private $rolePermissions;
private $cache;
private $adapter;
private $columnFilters;
public function __construct(
TableGatewayInterface $roles,
TableGatewayInterface $rolePermissions,
StorageInterface $cache,
ColumnFiltersInterface $columnFilters
)
{
$this->roles = $roles;
$this->rolePermissions = $rolePermissions;
$this->cache = $cache;
$this->adapter = $roles->getAdapter();
$this->columnFilters = $columnFilters;
$this->conn = $this->adapter->getDriver()->getConnection();
}
public function findAllBySelect()
{
$sql = new Sql($this->adapter);
$select = $sql->select();
$select->columns([
'id' => 'roleId',
'roleKey',
'roleName',
'roleLevel',
]);
$select->from(['r' => 'roles']);
return $select;
}
public function findAllByPaging(array $get)
{
$select = $this->findAllBySelect();
$this->columnFilters->clear();
$this->columnFilters->setColumns([
'roleKey',
'roleName',
'roleLevel',
]);
$this->columnFilters->setData($get);
$this->columnFilters->setSelect($select);
if ($this->columnFilters->searchDataIsNotEmpty()) {
$nest = $select->where->nest();
foreach ($this->columnFilters->getSearchData() as $col => $words) {
$nest = $nest->or->nest();
foreach ($words as $str) {
$nest->or->like(new Expression($col), '%'.$str.'%');
}
$nest = $nest->unnest();
}
$nest->unnest();
}
if ($this->columnFilters->orderDataIsNotEmpty()) {
foreach ($this->columnFilters->getOrderData() as $order) {
$select->order(new Expression($order));
}
}
// echo $select->getSqlString($this->adapter->getPlatform());
// die;
$paginatorAdapter = new DbSelect(
$select,
$this->adapter
);
$paginator = new Paginator($paginatorAdapter);
return $paginator;
}
}
In addition to the filters that appear by default, you may also need some built-in filters that the user cannot change through the user interface. Use the filter feature for this. This is a simple key/value object that will be automatically sent to your data provider and combined with other active filters.
Any filters you add are active by default. These active filters and columns can be customized by the user with show/hide actions in the settings tab. In addition to global search, VaDataTableServer also supports advanced custom filters with many supported entries as shown here:
Supported inputs for filtering are as follows:
Use filter properties to define new filters. Here's a code sample usage of these advanced filters:
<script>
export default {
props: ["resource", "title"],
data() {
return {
loading: false,
yearId: new Date().getFullYear(),
filters: [
{
source: "companyId",
type: "select",
col: 2,
attributes: {
optionText: "name",
multiple: true,
reference: "companies",
}
},
{
source: "jobTitleId",
type: "select",
col: 2,
attributes: {
optionText: "name",
multiple: true,
reference: "jobtitles",
}
},
{
source: "gradeId",
type: "select",
returnObject: false, // sends ids as array
col: 3,
attributes: {
optionText: "name",
multiple: true,
reference: "employeegrades",
}
},
],
};
}
};
</script>
For the input type, you should mainly use the type as well as the mandatory source attribute. Use the attributes property to combine specific attributes with the input component.
See all supported domain features:
Property | Type | Description |
---|---|---|
source | string |
Source property to display. |
col | number |
Determines the number of columns of the filter, i.e. its width for multi-device support. |
type | string |
The input type to use. |
returnObject | boolean |
If you set this value to false, the id value(s) are sent in the array, not in the object. |
label | string |
The column header uses the localized attribute source by default. |
labelKey | string |
Overrides the default source to i18n key message. |
attributes | object |
You must define any attributes or properties that will be combined with the input component within this object. |
If you want another filter(s) to change interactively after selecting a filter, take a look at the code in the following example.
<script>
export default {
props: ["resource", "title"],
data() {
return {
filters: [
{
source: "yearId",
type: "select",
attributes: {
optionText: "name",
multiple: false,
reference: "years",
},
label: this.$t("employees.yearId"),
},
{
source: "salaryListId",
type: "select",
attributes: {
optionText: "name",
multiple: false,
reference: "salarylists",
},
key: "yearId",
filters: ['yearId'],
label: this.$t("salaries.salaryListId"),
},
]
};
},
};
</script>
If you want to filter a date column between two specified date columns, you must add Start and End to the end of the column name.
In the following example, two date filters named attemptedAtStart and attemptedAtEnd are added for the attemptedAt column name.
<script>
export default {
props: ["resource", "title"],
data() {
return {
filters: [
{
source: "username",
type: "text",
},
{
source: "attemptedAtStart",
type: "date",
},
{
source: "attemptedAtEnd",
type: "date",
}
],
fields: [
{
source: "username",
type: "text",
sortable: true,
width: "10%"
},
{
source: "attemptedAt",
type: "date",
sortable: true,
width: "10%"
},
],
};
},
};
</script>
This VaList component comes with only one global action called create. The Create button will only appear if the current resource has a create action and the authenticated user has create permission on that resource.
Action Events
You don't have to follow the default redirect behavior. If you prefer a create action, simply subscribe to the action event and disable redirect generation with the disableCreateRedirect property to prevent the create button from redirecting to the linked action page. The same behavior applies to the show, edit and clone actions inside the VaDataTableServer. If you need a custom behavior within an Aside or dialog, use the item-action event and disable the default redirect. Note that all these keys will be automatically hidden if no action is taken for the relevant keys. Disabling each relevant action redirect will force the keys to reappear.
These action events will always provide you with the adapted CRUD header as well as the refreshed element from the API.
If you need other item actions in addition to the standard actions, use the special item.actions slot. For an updatable data table, you should use the row.actions slot.
<template>
<va-list
:fields="fields"
>
<va-data-table-server>
<template v-slot:[`item.actions`]="{ resource, item }">
<va-my-custom-button
:resource="resource"
:item="item"
></va-my-custom-button>
</template>
</va-data-table-server>
</va-list>
</template>
Data listing supports all kinds of bulk operations, whether copying or deleting. This feature will use your data provider's copyMany, updateMany and deleteMany methods. When you select some items, all available bulk actions will appear in the header.
By default Olobase Admin provides the bulk delete action, but you can add multiple bulk actions as required using the VaBulkActionButton which will use the bulk.actions slots and updateMany . This last component needs a necessary action prop that will be the object to send to your API. This object will contain all the properties you want to bulk update. The next example will show you an example of a bulk publish/unpublish bulk action:
<template>
<va-list
:filters="filters"
:fields="fields"
:items-per-page="10"
>
<template v-slot:bulk.actions="{ selected }">
<va-bulk-action-button
:label="$t('users.enable')"
icon="mdi-publish"
color="success"
:value="selected"
:action="{ active: true }"
text
></va-bulk-action-button>
<va-bulk-action-button
:label="$t('users.disable')"
icon="mdi-download"
color="orange"
:value="selected"
:action="{ active: false }"
text
></va-bulk-action-button>
</template>
<va-data-table-server
row-create
row-show
row-edit
disable-clone
disable-show
disable-edit
>
</va-data-table-server>
</va-list>
</template>