본문 바로가기
프로그래밍/nodejs

adminJS 기초 - 액션 (공식 사이트 번역)

by 한코코 2022. 11. 15.

Action

액션은 적절한 뷰를 표시하고 그 이면의 로직을 처리하는 역할을 합니다.

 

 

 

소개

AdminJS에는 각 리소스에 대해 정의된 7개의 주요 기본 작업이 있습니다. AdminJS 패널에 있는 모든 작업(기본 작업이든 사용자 지정 작업이든)에는 자동으로 생성된 REST API 엔드포인트도 있습니다. = /resources/{resourceId}/actions/{action}

모든 AdminJS 액션들은 다음과 같이 분류할 수 있습니다.

  • 리소스 작업
  • 액션 기록
  • 일괄 작업

리소스 작업은 지정된 리소스(위 Filter 버튼)의 콘텐츠 보기 헤더에서 엑세스(기억 장치에 데이터를 쓰거나 기억 장치에 들어있는 데이터를 탐색하고 읽는 과정) 할 수 있습니다.

레코드 작업은 목록의 레코드 옆이나 다른 레코드 작업의 콘텐츠 위에 있는 점 3개 메뉴를 통해 액세스 할 수 있습니다.

레코드를 하나 이상 선택하면 목록 작업의 테이블 머리글에 대량 유형 작업이 나타납니다.

 

리소스 유형 작업

이는 매개 변수 가 필요하지 않은 action입니다 recordId. 즉, 특정 레코드가 아닌 전체 리소스와 관련이 있습니다.
  • list리소스 아래에 레코드를 나열하고 필터링하는 일을 담당합니다.
  • search쿼리 문자열로 지정된 리소스의 레코드를 검색할 수 있습니다(기본적으로 제목 속성임).
  • new주어진 리소스에 새 레코드를 생성하는 일을 담당합니다.
 

레코드 유형 작업

recordId 매개변수가 필요한 action입니다. 특정 레코드와 관련이 있습니다.
  • show는 레코드의 세부 정보를 표시하는 역할을 합니다.
  • edit 지정된 레코드를 수정할 수 있습니다.
  • delete는 단일 레코드 삭제를 담당합니다.

 

대량 유형 작업

이들은 레코드의 ID 목록인 recordIds[] 매개변수가 필요한 action입니다.
  • bulkDelete는 데이터베이스에서 선택된 모든 레코드를 삭제합니다.
 
 
 
 
 

사용자 지정 actions

각 기본 AdminJs action은 완전히 사용자 정의할 수 있으며 새 사용자 정의 action을 생성할 수도 있습니다.
 

사용자 지정 작업 만들기

AdminJS에서 새 작업을 만드는 것은 매우 간단합니다. 리소스의 옵션 작업 구성에 새 작업 키를 추가하기만 하면 됩니다.
const UserResource = {
  resource: User,
  options: {
    actions: {
      myCustomAction: {
        actionType: 'record',
        component: false,
        handler: (request, response, context) => {
          const { record, currentAdmin } = context
          return {
            record: record.toJSON(currentAdmin),
            msg: 'Hello world',
          }
        },
      },
    },
  },
}

 

모든 사용자 지정 작업에는 레코드, 리소스 또는 대량일 수 있는 actionType을 지정해야 합니다. 또한 작업에 대한 핸들러를 정의해야 합니다. 이것은 데이터를 가져오거나 처리하기 위한 백엔드 방법입니다. 쿼리 및 변경 프로세스 모두에 단일 핸들러가 사용되지만 request.method(게시 또는 가져오기)를 기반으로 이들을 구별할 수 있습니다.

 

핸들러는 대량 또는 리소스 작업의 경우 레코드를 반환하거나 레코드 작업의 경우 레코드를 반환해야 합니다. 레코드/레코드와 함께 추가 정보를 반환할 수 있습니다.

 

마지막으로 중요한 옵션은 구성 요소입니다. 버튼을 누르면 백엔드 핸들러가 트리거되거나 렌더링될 사용자 정의 구성 요소를 제공할 수 있음을 의미하는 false일 수 있습니다.

 

action과 커스텀 컴포넌트

// my-custom-action.tsx
import React from 'react'
import { Box, H3 } from '@adminjs/design-system'
import { ActionProps } from 'adminjs'

const MyCustomAction = (props: ActionProps) => {
  const { record } = props

  return (
    <Box flex>
      <Box variant="white" width={1 / 2} boxShadow="card" mr="xxl" flexShrink={0}>
        <H3>Example of a simple page</H3>
        <p>Where you can put almost everything</p>
        <p>like this:</p>
        <p>
          <img src="https://i.redd.it/rd39yuiy9ns21.jpg" alt="stupid cat" width={300} />
        </p>
      </Box>
      <Box>
        <p>Or (more likely), operate on a returned record:</p>
        <Box overflowX="auto">{JSON.stringify(record)}</Box>
      </Box>
    </Box>
  )
}

export default MyCustomAction

options code 리소스 : 

const UserResource = {
  resource: User,
  options: {
    actions: {
      myCustomAction: {
        actionType: 'record',
        component: Components.MyCustomAction, // see "Writing your own Components"
        handler: (request, response, context) => {
          const { record, currentAdmin } = context
          return {
            record: record.toJSON(currentAdmin),
            msg: 'Hello world',
          }
        },
      },
    },
  },
}

필수 구성 외에도 다른 선택적 옵션을 지정할 수도 있습니다. 이 섹션의 뒷부분에서 설명합니다.

 

 

action의 가시성 및 접근성 변경

AdminJS에서는 기본적으로 모든 작업에 액세스하고 볼 수 있습니다. 작업이 표시된다는 것은 UI에서 액세스할 수 있음을 의미합니다. 액세스 가능한 작업은 보이지 않더라도 AdminJS API를 통해 액세스할 수 있는 작업입니다.

isVisible 옵션을 사용하여 작업의 가시성을 변경하거나 isAccessible 옵션을 사용하여 액세스 가능성을 변경할 수 있습니다. 구성은 동일하며 bool 값일 수 있습니다.

const UserResource = {
  resource: User,
  options: {
    actions: {
      edit: {
        isAccessible: false,
        isVisible: true,
      },
    },
  },
}

레코드의 context에 엑세스 할 수 있는 IsFuntion일 수도 있습니다.

const UserResource = {
  resource: User,
  options: {
    actions: {
      edit: {
        isAccessible: (context) => {
          const { record, currentAdmin } = context
          
          // We are only allowing to edit records created by currently logged in user
          return record?.params?.createdByUserId === currentAdmin.id
        },
        isVisible: true,
      },
    },
  },
}

레코드의 작업의 경우 접근성 및 가시성 검사는 예를 들어 특정 레코드를 로드할때 런타임에 수행됩니다. 리소스 작업의 접근성 및 가시성은 AdminJS 인스턴스가 생성될 때 평가합니다.

 

 

 

리소스 작업에 필터 버튼 표시

기본 목록 작업 테이블 위에 있는 필터 단추는 자신의 리소스 작업 페이지에도 표시될 수 있습니다. 그러나 기본적으로 비활성화되어 있습니다. 필터 버튼을 표시하려면 showFilter 옵션을 사용해야 합니다. 필터는 목록 작업에서와 정확히 동일하게 작동합니다. 필터를 설정하면 브라우저 주소에 추가되며 리소스 작업의 구성 요소에서 할당된 필터를 처리하는 것은 사용자의 책임입니다.

 

showFilter 옵션을 사용하여 목록 작업에 대한 필터를 숨길 수도 있습니다.

const UserResource = {
  resource: User,
  options: {
    actions: {
      list: {
        showFilter: false,
      },
    },
  },
}

 

 

작업 버튼을 누를 때 확인 메시지 표시

이것은 AdminJS 데모 애플리케이션에서 레코드를 삭제하려고 시도할 때 볼 수 있는 기능입니다. 삭제를 누르면 기록 삭제를 확인하는 브라우저의 확인 팝업이 나타납니다. 사용자 지정 작업에서 유사한 동작을 원하면 보호 옵션을 사용해야 합니다.

const UserResource = {
  resource: User,
  options: {
    actions: {
      myCustomAction: {
        actionType: 'record',
        component: false,
        handler: (request, response, context) => {
          const { record, currentAdmin } = context
          return {
            record: record.toJSON(currentAdmin),
          }
        },
        guard: 'doYouReallyWantToDoThis',
      },
    },
  },
}

guard 메세지는 번역 키를 사용할 수 있도록 현지화를 거칩니다.

 

 

"이전(before)" 및 "이후(after)" 훅(Hook) 사용

핸들러 외에도 사용자 지정 또는 기존 작업에서 이전 및 이후 Hook을 정의할 수 있습니다. before Hook은 작업 시작 시 isAccessible 및 핸들러 이전에 트리거됩니다. after Hook은 액션이 끝날 때 트리거됩니다.

 

아래 예에서는 활성 상태인 사용자 리소스 결과만 표시하는 기본 필터를 설정하기 위해 이전 Hook In 목록 작업을 사용합니다. 또한 핸들러가 반환한 응답의 메타 속성을 console.log에 연결한 후 사용할 것입니다.

const customBefore = (request, context) => {
  const { query = {} } = request
  const newQuery = {
    ...query,
    ['filters.status']: 'active',
  }
  
  request.query = newQuery
  
  return request
}

const customAfter = (originalResponse, request, context) => {
  console.log(originalResponse.meta)
  
  return originalResponse
}

const UserResource = {
  resource: User,
  options: {
    actions: {
      list: {
        before: [customBefore],
        after: [customAfter],
      },
    },
  },
}

before 및 after 옵션은 Before 및 After 함수 목록을 허용합니다. 이는 모든 Hook이 before Hook에 의해 수정될 수 있는 인수를 사용하기 때문에 순서가 중요한 작업에 여러 before/after Hook을 할당할 수 있음을 의미합니다.

 

before Hook은 예를 들어 편집 작업에서 추가 유효성 검사를 추가하는 데 사용할 수도 있습니다.

// other imports
import { ValidationError } from 'adminjs'

const validateForm = (request, context) => {
  const { payload = {}, method } = request
  
  // We only want to validate "post" requests
  if (method !== 'post') return request
  
  // Payload contains data sent from the frontend
  const { age = null, lastName = '' } = payload
  
  // We will store validation errors in an object, so that
  // we can throw multiple errors at the same time
  const errors = {}
  
  // We are doing validations and assigning errors to "errors" object
  if (!age || age < 18) {
    errors.age = {
      message: 'A user must be at least 18 years old',
    }
  }
  
  if (lastName.trim().length === 0) {
    errors.lastName = {
      message: 'Last name is required',
    }
  }
  
  // We throw AdminJS ValidationError if there are errors in the payload
  if (Object.keys(errors).length) {
    throw new ValidationError(errors)
  }
  
  return request
}

const UserResource = {
  resource: User,
  options: {
    actions: {
      edit: {
        before: [validateForm],
      },
    },
  },
}

오류는 이제 편집 작업의 양식에서 강조 표시됩니다.

 

 

드로어(drawer)에 레코드 작업 표시

기본적으로 모든 기록 작업은 새 페이지에 표시됩니다. 그러나 필터 drawer를 여는 것과 유사하게 화면 오른쪽에 있는 drawer에 표시할 수 있습니다. 이는 showInDrawer 옵션을 사용하여 달성할 수 있습니다.

const UserResource = {
  resource: User,
  options: {
    actions: {
      myCustomAction: {
        actionType: 'record',
        component: Components.MyCustomAction, // see "Writing your own Components"
        handler: (request, response, context) => {
          const { record, currentAdmin } = context
          return {
            record: record.toJSON(currentAdmin),
          }
        },
        showInDrawer: true,
      },
    },
  },
}

 

 

 

레코드 작업이 너무 많을 때 상황에 맞는 메뉴에 레코드 작업 중첩

많은 사용자 지정 레코드 작업을 정의했지만 화면 너비에 맞지 않는 상황이 발생하면 parent 옵션을 사용하는 것이 해결책이 될 수 있습니다. 이 옵션을 사용하면 해당 작업을 그 아래에 그룹화할 드롭다운 버튼의 이름을 설정할 수 있습니다.

const UserResource = {
  resource: User,
  options: {
    actions: {
      myCustomAction: {
        actionType: 'record',
        component: false,
        handler: (request, response, context) => {
          const { record, currentAdmin } = context
          return {
            record: record.toJSON(currentAdmin),
          }
        },
        parent: 'More',
      },
      myOtherCustomAction: {
        actionType: 'record',
        component: false,
        handler: (request, response, context) => {
          const { record, currentAdmin } = context
          return {
            record: record.toJSON(currentAdmin),
          }
        },
        parent: 'More',
      },
    },
  },
}

위의 예에는 myCustomAction 및 myOtherCustomAction의 두 가지 사용자 지정 작업이 있으며 둘 다 자세히 드롭다운 버튼 아래에 그룹화되어 있습니다.

댓글