Interface

Action

Action

Interface representing an Action in AdminBro. Look at 04. Customize actions to see where you can use this interface.

Example Action

const action = {
  actionType: 'record',
  icon: 'View',
  isVisible: true,
  handler: async () => {...},
  component: AdminBro.bundle('./my-action-component'),
}

There are 3 kinds of actions:

  1. Resource action, which is performed for an entire resource.
  2. Record action, invoked for an record in a resource
  3. Bulk action, invoked for an set of records in a resource

...and there are 7 actions predefined in AdminBro

  1. new (resource action) - create new records in a resource
  2. list (resource action) - list all records within a resource
  3. search (resource action) - search by query string
  4. edit (record action) - update records in a resource
  5. show (record action) - show details of given record
  6. delete (record action) - delete given record
  7. bulkDelete (bulk action) - delete given records

Users can also create their own actions or override those already existing by using ResourceOptions

const AdminBroOptions = {
  resources: [{
    resource: User,
    options: {
      actions: {
        // example of overriding existing 'new' action for
        // User resource.
        new: {
          icon: 'Add'
        },
        // Example of creating a new 'myNewAction' which will be
        // a resource action available for User model
        myNewAction: {
          actionType: 'resource',
          handler: async (request, response, context) => {...}
        }
      }
    }
  }]
}

const { ACTIONS } = require('admin-bro')
// example of adding after filter for 'show' action for all resources
ACTIONS.show.after = async () => {...}

Members

'resource' | 'record' | 'bulk'

# actionType

Type of an action - could be either resource, record or bulk.

When you define a new action - it is required.

View Source admin-bro/src/backend/actions/action.interface.ts, line 287

After.<T> | Array.<After.<T>>

# after Optional

After action hook. When it is given - it is performed on the returned, by handler function response.

You can use it to (just an idea)

  • create log of changes done in the app
  • prefetch additional data after original Handler is being performed

Creating a changelog example:

// example mongoose model
const ChangeLog = mongoose.model('ChangeLog', mongoose.Schema({
  // what action
  action: { type: String },
  // who
  userId: { type: mongoose.Types.ObjectId, ref: 'User' },
  // on which resource
  resource: { type: String },
  // was record involved (resource and recordId creates to polymorphic relation)
  recordId: { type: mongoose.Types.ObjectId },
}, { timestamps: true }))

// actual after function
const createLog = async (originalResponse, request, context) => {
  // checking if object doesn't have any errors or is a delete action
  if ((request.method === 'post'
       && originalResponse.record
       && !Object.keys(originalResponse.record.errors).length)
       || context.action.name === 'delete') {
    await ChangeLog.create({
      action: context.action.name,
      // assuming in the session we store _id of the current admin
      userId: context.currentAdmin && context.currentAdmin._id,
      resource: context.resource.id(),
      recordId: context.record && context.record.id(),
    })
  }
  return originalResponse
}

// and attaching this function to actions for all resources
const { ACTIONS } = require('admin-bro')

ACTIONS.edit.after = createLog
ACTIONS.delete.after = createLog
ACTIONS.new.after = createLog

View Source admin-bro/src/backend/actions/action.interface.ts, line 400

Before | Array.<Before>

# before Optional

Before action hook. When it is given - it is performed before the Action#handler method.

Example of hashing password before creating it:

actions: {
  new: {
    before: async (request) => {
      if(request.payload.password) {
        request.payload = {
          ...request.payload,
          encryptedPassword: await bcrypt.hash(request.payload.password, 10),
          password: undefined,
        }
      }
      return request
    },
  }
}

View Source admin-bro/src/backend/actions/action.interface.ts, line 374

string | false

# component Optional

Component which will be used to render the action. To pass the component use AdminBro.bundle method.

Action components accepts ActionProps and are rendered by the BaseActionComponent

When component is set to false then action doesn't have it's own view. Instead after clicking button it is immediately performed. Example of an action without a view is module:DeleteAction.

View Source admin-bro/src/backend/actions/action.interface.ts, line 331

string | number | Array.<(string|number)>

# containerWidth Optional

The max width of action HTML container. You can put here an actual size in px or an array of widths, where different values will be responsible for different breakpoints. It is directly passed to action's wrapping Box component, to its width property.

Examples


// passing regular string
containerWidth: '800px'

// passing number for 100% width
containerWidth: 1

// passing values for different breakpoints
containerWidth: [1, 1/2, 1/3]

View Source admin-bro/src/backend/actions/action.interface.ts, line 469

string

# guard Optional

guard message - user will have to confirm it before executing an action.

new AdminBro({ resources: [{
  resource: Car,
  options: { actions: {
    delete: {
      guard: 'doYouReallyWantToDoThis',
    }
  }}
}]})

What you enter there goes to TranslateFunctions#translateMessage function, so in order to define the actual message you will have to specify its translation in AdminBroOptions.Locale

View Source admin-bro/src/backend/actions/action.interface.ts, line 310

ActionHandler.<T> | Array.<ActionHandler.<T>>

# handler

handler function which will be invoked by either:

If you are defining this action for a record it has to return:

// Handler of a 'record' action
handler: async (request, response, context) {
  const user = context.record
  const Cars = context._admin.findResource('Car')
  const userCar = Car.findOne(context.record.param('carId'))
  return {
    record: user.toJSON(context.currentAdmin),
  }
}

Required for new actions. For modifying already defined actions like new and edit we suggest using Action#before and Action#after hooks.

View Source admin-bro/src/backend/actions/action.interface.ts, line 345

boolean

# hideActionHeader Optional

Indicates if Action Header should be hidden. Action header consist of:

  • breadcrumbs
  • action buttons
  • action title

View Source admin-bro/src/backend/actions/action.interface.ts, line 459

string

# icon Optional

icon name for the action. Take a look Icon component, because what you put here is passed down to it.

new AdminBro({ resources: [{
  resource: Car,
  options: { actions: { edit: { icon: 'Add' } } },
}]})

View Source admin-bro/src/backend/actions/action.interface.ts, line 296

boolean | IsFunction

# isAccessible Optional

Indicates if the action can be invoked for given invocation context. You can pass a boolean or function of type IsFunction, which takes ActionContext as an argument.

You can use it as a carrier between the hooks.

Example for isVisible function which allows the user to edit cars which belongs only to her:

const canEditCars = ({ currentAdmin, record }) => {
  return currentAdmin && (
    currentAdmin.role === 'admin'
    || currentAdmin._id === record.param('ownerId')
  )
}

new AdminBro({ resources: [{
  resource: Car,
  options: { actions: { edit: { isAccessible: canEditCars } } }
}]})
See:

View Source admin-bro/src/backend/actions/action.interface.ts, line 237

boolean | IsFunction

# isVisible Optional

indicates if action should be visible for given invocation context. It also can be a simple boolean value. True by default. The most common example of usage is to hide resources from the UI. So let say we have 2 resources User and Cars:

const User = mongoose.model('User', mongoose.Schema({
  email: String,
  encryptedPassword: String,
}))
const Car = mongoose.model('Car', mongoose.Schema({
  name: String,
  ownerId: { type: mongoose.Types.ObjectId, ref: 'User' },
})

so if we want to hide Users collection, but allow people to pick user when creating cars. We can do this like this:

new AdminBro({ resources: [{
  resource: User,
  options: { actions: { list: { isVisible: false } } }
}]})

In contrast - when we use Action#isAccessible instead - user wont be able to pick car owner.

See:

View Source admin-bro/src/backend/actions/action.interface.ts, line 201

LayoutElementFunction | Array.<LayoutElement>

# layout Optional

Definition for the layout. Works with the edit and show actions.

With the help of LayoutElement you can put all the properties to whatever layout you like, without knowing React.

This is an example of defining a layout

const layout = [{ width: 1 / 2 }, [
    ['@H3', { children: 'Company data' }],
    'companyName',
    'companySize',
  ]],
  [
    ['@H3', { children: 'Contact Info' }],
    [{ flexDirection: 'row', flex: true }, [
      ['email', { pr: 'default', flexGrow: 1 }],
      ['address', { flexGrow: 1 }],
    ]],
  ],
]

Alternatively you can pass a function taking CurrentAdmin as an argument. This will allow you to show/hide given property for restricted users.

To see entire documentation and more examples visit LayoutElement

See:

View Source admin-bro/src/backend/actions/action.interface.ts, line 491

string

# name

Name of an action which is its uniq key. If you use one of list, search, edit, new, show, delete or bulkDelete you override existing actions. For all other keys you create a new action.

View Source admin-bro/src/backend/actions/action.interface.ts, line 193

boolean

# showFilter Optional

if filter should be visible on the sidebar. Only for resource actions

Example of creating new resource action with filter

new AdminBro({ resources: [{
  resource: Car,
  options: { actions: {
    newAction: {
      type: 'resource',
      showFilter: true,
    }
  }}
}]})

View Source admin-bro/src/backend/actions/action.interface.ts, line 267

boolean

# showInDrawer Optional

Indicates if given action should be seen in a drawer or in a full screen. Default to false

View Source admin-bro/src/backend/actions/action.interface.ts, line 453

Type Definitions

object

# ActionContext

Execution context for an action. It is passed to the Action#handler, Action#before and Action#after functions.

Apart from the properties defined below it also extends TranslateFunctions. So you can use i.e. context.translateMessage(...) and others...

Properties:
Name Type Attributes Description
{...} TranslateFunction

all functions from TranslateFunctions interface.

_admin AdminBro

current instance of AdminBro. You may use it to fetch other Resources by their names:

resource BaseResource

Resource on which action has been invoked. Null for dashboard handler.

record BaseRecord <optional>

Record on which action has been invoked (only for actionType === 'record')

records Array.<BaseRecord> <optional>

Records on which action has been invoked (only for actionType === 'bulk')

h ViewHelpers

view helpers

action ActionDecorator

Object of currently invoked function. Not present for dashboard action

currentAdmin CurrentAdmin <optional>

Currently logged in admin

{...} any

Any custom property which you can add to context

View Source admin-bro/src/backend/actions/action.interface.ts, line 13

# async ActionHandler(request, response, context) → {Promise.<T>}

Type of a handler function. It has to return response compatible with ActionResponse, BulkActionResponse or RecordActionResponse

Parameters:
Name Type Description
request ActionRequest
response any
context ActionContext

View Source admin-bro/src/backend/actions/action.interface.ts, line 94

Promise.<T>
object

# ActionRequest

ActionRequest

Properties:
Name Type Attributes Description
params object

parameters passed in an URL

resourceId string

Id of current resource

recordId string <optional>

Id of current record (in case of record action)

recordIds string <optional>

Id of selected records (in case of bulk action) divided by commas

action string

Name of an action

query string <optional>

an optional search query string (for search resource action)

{...} any
payload Record.<string, any> <optional>

POST data passed to the backend

query Record.<string, any> <optional>

Elements of query string

method 'post' | 'get'

HTTP method

View Source admin-bro/src/backend/actions/action.interface.ts, line 45

object

# ActionResponse

Base response for all actions

Properties:
Name Type Attributes Description
notice NoticeMessage <optional>

Notice message which should be presented to the end user after showing the action

redirectUrl string <optional>

redirect path

{...} any

Any other custom parameter

View Source admin-bro/src/backend/actions/action.interface.ts, line 61

# async After(response, request, context)

Type of an after hook action.

Parameters:
Name Type Description
response T

Response returned by the default ActionHandler

request ActionRequest

Original request which has been sent to ActionHandler

context ActionContext

Invocation context

View Source admin-bro/src/backend/actions/action.interface.ts, line 118

# async Before(request, context) → {Promise.<ActionRequest>}

Before action hook. When it is given - it is performed before the ActionHandler method.

Parameters:
Name Type Description
request ActionRequest

Request object

context ActionContext

Invocation context

View Source admin-bro/src/backend/actions/action.interface.ts, line 107

Promise.<ActionRequest>
object

# BulkActionResponse

Required response of a Record action. Extends ActionResponse

Properties:
Name Type Description
records Array.<RecordJSON>

Array of RecordJSON objects.

View Source admin-bro/src/backend/actions/action.interface.ts, line 86

# IsFunction(context)

Defines the type of Action#isAccessible and Action#isVisible functions

Parameters:
Name Type Description
context ActionContext

View Source admin-bro/src/backend/actions/action.interface.ts, line 70

String | Array

# LayoutElement

LayoutElement is used to change the default layout of edit, show and new actions. You define the layout as an Array.<LayoutElement> and AdminBro renders it with React components.

You don't have to know React to create usable Layout for you actions but be sure to take a look at the possible Props which can be used to style the components. The most often used props are BoxProps, because Box is the default wrapper.

Available values for a LayoutElement type

To Action#layout you have to pass an Array.<LayoutElement>. Where each LayoutElement could have a different type defining its position and purpose.

Type definition

Those are available types for LayoutElement

Type Purpose Example
string It will be changed to the property in vertical layout layout: ['name']
Array.<string> It will be changed to the properties in vertical layout layout: [['name', 'surname']]
[string, BoxProps] property wrapped by Box component with BoxProps layout: [['name', {width: 1/2}]]
[BoxProps, Array.<LayoutElement>] Creates a Box and nest all the child LayoutElements inside. layout: [[{width: 1/2}, ['name', 'surname']]]
Array.<LayoutElement> For grouping LayoutElements inside a wrapper layout: [['name', {mt: 'xl'}], ['surname', , {ml: 'xl'}]]
[@ComponentName, PropsWithChildren] if you precede first item with "@" it will create component of this name layout: [['@Header', {children: 'User Data'}]]

Examples

Let say you have following properties in your database: companyName, email, address and companySize

1. The simplest horizontal layout:

const layout = [
 'companyName',
 'email',
 'address',
 'companySize',
]

generates:

2. Now Wrap everything with a Box of 2/3 max width and horizontal margin (mx) set to auto. This will center all inputs

const layout = [
  [{ width: 2 / 3, mx: 'auto' }, [
    'companyName',
    'email',
    'address',
    'companySize',
  ]],
]

generates:

Hint: you can also pass an array to width to define how it will behave in a different responsive breakpoints.

3. Add headers between sections

const layout = [
  [{ width: 2 / 3, mx: 'auto' }, [
    ['@H3', { children: 'Company data' }],
    'companyName',
    'companySize',
    ['@H3', { children: 'Contact Info' }],
    'email',
    'address',
  ]],
]

generates:

To inject content inside the given Component pass children props to it.

4. Make email and address 50% width

We will wrap them with a Box (default component) which is a flex. Then we will have to wrap also each of them with extra box to define paddings.

I will also align to left top section that by removing { mx: auto } and changing width to 1 / 2.

const layout = [{ width: 1 / 2 }, [
    ['@H3', { children: 'Company data' }],
    'companyName',
    'companySize',
  ]],
  [
    ['@H3', { children: 'Contact Info' }],
    [{ flexDirection: 'row', flex: true }, [
      ['email', { pr: 'default', flexGrow: 1 }],
      ['address', { flexGrow: 1 }],
    ]],
  ],
]

generates:

5. Lastly, take a look at the example with a function instead of LayoutElement.

const layout = currentAdmin => ([
 ['@MessageBox', {
   message: `Welcome ${currentAdmin && currentAdmin.email}`,
   children: 'On this page yo can do whatever you like',
   variant: 'info',
   mb: 'xxl',
 }],
 [
   'companyName',
   'companySize',
   'email',
   'address',
 ],
])

Generates following Show page:

View Source admin-bro/src/backend/utils/layout-element-parser.ts, line 74

# LayoutElementFunction(currentAdminopt) → {Array.<LayoutElement>}

Function returning Array used by Action#layout

Parameters:
Name Type Attributes Description
currentAdmin CurrentAdmin <optional>

View Source admin-bro/src/backend/utils/layout-element-parser.ts, line 219

Array.<LayoutElement>
object

# RecordActionResponse

Required response of a Record action. Extends ActionResponse

Properties:
Name Type Description
record RecordJSON

Record object.

View Source admin-bro/src/backend/actions/action.interface.ts, line 78