Dynamic has many relation
Allows e.g. pagination or filtering on HasMany relation.
DynamicHasMany.tsx
import { Component, EntitySubTree, HasMany, SugaredRelativeEntityList, useEntity, useExtendTree } from '@contember/admin'import React, { ReactNode, useEffect, useRef, useState } from 'react'import { useObjectMemo } from '@contember/react-utils'export type DynamicHasManyProps =& SugaredRelativeEntityList& { children: ReactNode }export const DynamicHasMany = Component<DynamicHasManyProps>(({ children, ...props }) => {const listParams = useObjectMemo(props)const [initialListParams] = useState(listParams)const [displayedState, setDisplayedState] = useState<{treeRoot: undefined | stringlist: SugaredRelativeEntityList} | undefined>(undefined)const extendTree = useExtendTree()const entity = useEntity()const requestRef = useRef(1)useEffect(() => {(async () => {if (initialListParams === listParams || !entity.idOnServer) {setDisplayedState(undefined)return}const request = ++requestRef.currentconst entityNode = (<EntitySubTree entity={{ entityName: entity.name, where: { id: entity.idOnServer } }}><HasMany {...listParams}>{children}</HasMany></EntitySubTree>)const treeRoot = await extendTree(entityNode)if (request !== requestRef.current) {return}setDisplayedState({ treeRoot, list: listParams })})()}, [children, entity.idOnServer, entity.name, extendTree, initialListParams, listParams])if (!displayedState) {return (<HasMany {...initialListParams}>{children}</HasMany>)}return (<EntitySubTree entity={{ entityName: entity.name, where: { id: entity.idOnServer! } }} treeRootId={displayedState.treeRoot}><HasMany {...displayedState.list}>{children}</HasMany></EntitySubTree>)}, ({ children, ...props }) => {return (<HasMany {...props}>{children}</HasMany>)})
usage.tsx
import { Button, Component, SugaredRelativeEntityList } from '@contember/admin'import { HubArticleListingItem } from './HubArticleListingItem'import React, { useMemo, useState } from 'react'import { DynamicHasMany } from './DynamicHasMany'const getArticleProps = (page: number): SugaredRelativeEntityList => {return {field: 'articles[publishedAt <= "now"]',orderBy: 'pinned desc, publishedAt desc',limit: 5,offset: (page - 1) * 5,}}export const ArticlePagination = Component(() => {const [page, setPage] = useState(1)return (<><div className="space-y-8"><DynamicHasMany {...useMemo(() => getArticleProps(page), [page])} ><HubArticleListingItem /></DynamicHasMany></div><div className="flex gap-2 justify-center mt-4"><Button onClick={() => setPage(page - 1)} disabled={page === 1}>Previous page</Button><Button onClick={() => setPage(page + 1)}>Next page</Button></div></>)}, () => {return (<DynamicHasMany {...getArticleProps(1)} ><HubArticleListingItem /></DynamicHasMany>)})