Standard pages tuning
Fields Grouping
In some cases, you may want to organize data fields into specific groups for better structure and clarity. For example, you could create a "Main Info" group to include columns like title, description, country, and apartment_image. Another group, "Characteristics," could hold attributes such as price, square_meter, number_of_rooms, and listed. Any values without a specified group will be categorized under "Other.
export default {
...
options: {
...
fieldGroups: [
{
groupName: 'Main info',
columns: ['id','title', 'description', 'country', 'apartment_image']
},
{
groupName: 'Characteristics',
columns: ['price', 'square_meter', 'number_of_rooms', "listed"]
}
],
}
}
Here is how it looks:
You can also specify on which page you want to create or delete groups. If you assign null, the groups will disappear from this page.
export default {
...
options: {
createFieldGroups: [
{
groupName: 'Main info',
columns: ['id','title']
},
{
groupName: 'Characteristics',
columns: ['description', 'country', 'price', 'square_meter', 'number_of_rooms', "listed"]
}
],
editFieldGroups: null,
showFieldGroups: null,
}
}
List
Default Sorting
import { AdminForthSortDirections } from 'adminforth';
...
export default {
resourceId: 'aparts',
options: {
defaultSort: {
columnName: 'created_at',
direction: AdminForthSortDirections.asc,
}
}
}
Page size
use options.listPageSize
to define how many records will be shown on the page
export default {
resourceId: 'aparts',
options: {
...
listPageSize: 10,
}
}
]
Custom row click action
By default, when you click on a record in the list view, the show view will be opened.
You can change this behavior by using options.listTableClickUrl
.
To disable any action (don't open show) return null:
export default {
resourceId: 'aparts',
options: {
...
listTableClickUrl: async (record, adminUser) => null,
}
}
]
To open a custom page, return URL to the custom page (can start with https://, or relative adminforth path):
options: {
...
listTableClickUrl: async (record, adminUser) => {
return `https://google.com/search?q=${record.name}`;
}
}
If you wish to open the page in a new tab, add target=_blank
get param to the returned URL:
options: {
...
listTableClickUrl: async (record, adminUser) => {
return `https://google.com/search?q=${record.name}&target=_blank`;
}
}
Auto-refresh records
options.listRowsAutoRefreshSeconds
might be used to silently refresh records that are loaded (no new records will be fetched if
they appear)
export default {
resourceId: 'aparts',
hooks: {
list: {
afterDatasourceResponse: async ({ response }: { response: any }) => {
response.forEach((r: any) => {
// substitute random country on any load
const countries = [ 'US', 'DE', 'FR', 'GB', 'NL', 'IT', 'ES', 'DK', 'PL', 'UA',
'CA', 'AU', 'BR', 'JP', 'CN', 'IN', 'KR', 'TR', 'MX', 'ID']
r.country = countries[Math.floor(Math.random() * countries.length)];
})
return { ok: true, error: "" }
}
}
},
options: {
...
listRowsAutoRefreshSeconds: 1,
}
}
]
Creating
Fill with default values
Sometimes you want to generate some field value without asking user to fill it. For example createdAt oftenly store time of creation of the record. You can do this by using fillOnCreate:
export default {
name: 'apartments',
fields: [
...
{
name: 'created_at',
type: AdminForthDataTypes.DATETIME,
fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),
},
],
},
...
],
Also you can assign adminUser ID by adminUser.dbUser.id
:
export default {
name: 'apartments',
fields: [
...
{
name: 'created_by',
type: AdminForthDataTypes.STRING,
fillOnCreate: ({ initialRecord, adminUser }) => adminUser.dbUser.id,
},
],
},
...
],
Same effect can be achieved by using hooks. But
fillOnCreate
might be shorter and more readable.
Link to create form with preset values
Sometimes you might need to create a link that will open the create form with some fields pre-filled. For example, you might want to create a link that will open the create form with the realtor_id field pre-filled with the current user's ID.
<template>
...
<LinkButton
:to="{
name: 'resource-create',
params: {
resourceId: 'aparts',
},
query: {
values: encodeURIComponent(JSON.stringify({
realtor_id: coreStore?.adminUser.dbUser.id
})),
},
}"
>
{{$t('Create new apartment')}}
</LinkButton>
...
</template>
<script setup lang="ts">
import { LinkButton } from '@afcl';
import { useCoreStore } from '@/stores/core';
const coreStore = useCoreStore();
</script>
Editing
You can set a column editReadonly
so it will be shown in the edit form but will be disabled.
This might be useful to better identify the record during editing or to show some additional information that should not be changed but can help to edit the record.
export default {
name: 'apartments',
fields: [
...
{
name: 'created_at',
type: AdminForthDataTypes.DATETIME,
editReadonly: true,
},
],
},
...
],
editReadonly
is check enforced both on fronted and backend. So it is safe to use it to make sure that data will be never changes.
minValue and maxValue
You can add minValue
and maxValue
limits to columns, so it will show an error below an input when entered value is out of bounds.
export default {
name: 'apartments',
columns: [
...
{
name: 'square_meter',
label: 'Square',
minValue: 3,
maxValue: 1000,
},
],
},
...
],
minValue
andmaxValue
checks are enforced both on frontend and backend.
Validation
In cases when column values must follow certain format, you can add validation
to it.
validation
is an array of rules, each containing regExp
that defines a format for a value and message
that will be displayed in case when entered value does not pass the check.
export default {
name: 'adminuser',
columns: [
...
{
name: 'email',
required: true,
isUnique: true,
validation: [
{
regExp: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
message: 'Email is not valid, must be in format example@test.com',
},
],
},
],
},
...
],
validation
checks are enforced both on frontend and backend.
Input prefix and suffix
You can add prefix or suffix to inputs by adding inputPrefix
or inputSuffix
fields to a column.
export default {
name: 'users',
columns: [
...
{
name: "price",
inputSuffix: "USD",
allowMinMaxQuery: true,
},
],
},
...
],
These fields can only be used with following AdminForthDataTypes
: DECIMAL
, FLOAT
, INTEGER
, STRING
and JSON
(only if JSON
column is an array with appropriate itemType
).
Editing note
You can add editingNote
to a column to show a note below the input field.
export default {
name: 'adminuser',
columns: [
...
{
name: "password",
editingNote: { edit: "Leave empty to keep password unchanged" },
},
],
},
...
],
Filling an array of values
Whenever you want to have a column to store not a single value but an array of values you have to set column as AdminForthDataTypes.JSON
. This way when you are creating or editing a record you can type in a JSON array into a textfield. To simplify this process and allow you to create and edit separate items you can add isArray
to a column.
export default {
name: 'adminuser',
columns: [
...
{
name: "room_sizes",
type: AdminForthDataTypes.JSON,
isArray: {
enabled: true,
itemType: AdminForthDataTypes.FLOAT,
},
},
],
},
...
],
Doing so, will result in UI displaying each item of the array as a separate input corresponding to isArray.itemType
on create and edit pages.
itemType
value can be any of AdminForthDataTypes
except JSON
and RICHTEXT
.
By default it is forbidden to store duplicate values in an array column. To change that you can add allowDuplicateItems: true
to isArray
, like so:
export default {
name: 'adminuser',
columns: [
...
{
name: "room_sizes",
type: AdminForthDataTypes.JSON,
isArray: {
enabled: true,
itemType: AdminForthDataTypes.FLOAT,
allowDuplicateItems: true,
},
},
],
},
...
],
All validation rules, such as minValue
, maxValue
, minLength
, maxLength
and validation
will be applied not to array itself but instead to each item.
Note: array columns can not be marked as masked
, be a primaryKey
and at the time can not be linked to a foreign resource.
Foreign resources
When you want to create a connection between two resources, you need to add foreignResource
to a column, like so:
export default {
name: 'adminuser',
columns: [
...
{
name: "realtor_id",
foreignResource: {
resourceId: 'adminuser',
},
},
],
},
...
],
This way, when creating or editing a record you will be able to choose value for this field from a dropdown selector and on list and show pages this field will be displayed as a link to a foreign resource.
Filtering
Filter Options
You can specify the delay between filtering requests and filtering operator for a column using filterOptions
field.
export default {
name: 'adminuser',
columns: [
...
{
name: "title",
required: true,
maxLength: 255,
minLength: 3,
filterOptions: {
debounceTimeMs: 500,
substringSearch: false,
},
},
],
},
...
],
debounceTimeMs
field dictates how long (in milliseconds) to wait between inputs to send updated data request. By increasing this value, you can reduce the amount of requests set to backend. Default value for this field is set to 10ms.
substringSearch
sets what comparison operator to use for text field. By default this field is set to true
, which results in using case-insensitive ILIKE
operator, that will look for records that have filter string anywhere inside field value. Setting this substringSearch
to false
will result in using more strict EQ
operator, that will look for exact full-string matches.