你的问题已经在这里回答了,但我想告诉你我的解决方法,以便 React-Admin 处理多对多关系。
正如在提到的答案中所说,您必须扩展 DataProvider 才能获取多对多关系的资源。但是,您需要使用新的 REST 动词,让我们假设GET_MANY_MANY_REFERENCE您的应用程序的某个地方。由于不同的 REST 服务/API 可以具有不同的路由格式来获取相关资源,因此我没有费心尝试构建新的 DataProvider,我知道这不是一个很好的解决方案,但对于较短的截止日期来说是相当简单的。
我的解决方案是获取灵感并为多对多关系<ReferenceManyField>构建一个新组件。该组件使用fetch API<ReferenceManyManyField>获取相关记录。On response 使用响应数据构建对象,其中一个数据是一个对象,其键是记录 ID,值是相应的记录对象,以及一个具有记录 ID 的 ids 数组。这与其他状态变量(如 page、sort、perPage、total)一起传递给子项,以处理数据的分页和排序。请注意,更改 Datagrid 中数据的顺序意味着将向 API 发出新请求。这个组件分为一个控制器和一个视图,比如componentDidMount<ReferencemanyField>,其中控制器获取数据、管理数据并将其传递给子级,而视图接收控制器数据并将其传递给子级渲染其内容。这使我可以在 Datagrid 上呈现多对多关系数据,即使有一些限制,它是一个聚合到我的项目的组件,并且只有在发生某些更改时才使用我当前的 API,我必须将字段更改为,但就目前而言,它可以工作并且可以在我的应用程序中重复使用。
实施细节如下:
//ReferenceManyManyField
export const ReferenceManyManyField = ({children, ...prop}) => {
if(React.Children.count(children) !== 1) {
throw new Error( '<ReferenceManyField> only accepts a single child (like <Datagrid>)' )
}
return <ReferenceManyManyFieldController {...props}>
{controllerProps => (<ReferenceManyManyFieldView
{...props}
{...{children, ...controllerProps}} /> )}
</ReferenceManyManyFieldController>
//ReferenceManyManyFieldController
class ReferenceManyManyFieldController extends Component {
constructor(props){
super(props)
//State to manage sorting and pagination, <ReferecemanyField> uses some props from react-redux
//I discarded react-redux for simplicity/control however in the final solution react-redux might be incorporated
this.state = {
sort: props.sort,
page: 1,
perPage: props.perPage,
total: 0
}
}
componentWillMount() {
this.fetchRelated()
}
//This could be a call to your custom dataProvider with a new REST verb
fetchRelated({ record, resource, reference, showNotification, fetchStart, fetchEnd } = this.props){
//fetchStart and fetchEnd are methods that signal an operation is being made and make active/deactivate loading indicator, dataProvider or sagas should do this
fetchStart()
dataProvider(GET_LIST,`${resource}/${record.id}/${reference}`,{
sort: this.state.sort,
pagination: {
page: this.state.page,
perPage: this.state.perPage
}
})
.then(response => {
const ids = []
const data = response.data.reduce((acc, record) => {
ids.push(record.id)
return {...acc, [record.id]: record}
}, {})
this.setState({data, ids, total:response.total})
})
.catch(e => {
console.error(e)
showNotification('ra.notification.http_error')
})
.finally(fetchEnd)
}
//Set methods are here to manage pagination and ordering,
//again <ReferenceManyField> uses react-redux to manage this
setSort = field => {
const order =
this.state.sort.field === field &&
this.state.sort.order === 'ASC'
? 'DESC'
: 'ASC';
this.setState({ sort: { field, order } }, this.fetchRelated);
};
setPage = page => this.setState({ page }, this.fetchRelated);
setPerPage = perPage => this.setState({ perPage }, this.fetchRelated);
render(){
const { resource, reference, children, basePath } = this.props
const { page, perPage, total } = this.state;
//Changed basePath to be reference name so in children can nest other resources, not sure why the use of replace, maybe to maintain plurals, don't remember
const referenceBasePath = basePath.replace(resource, reference);
return children({
currentSort: this.state.sort,
data: this.state.data,
ids: this.state.ids,
isLoading: typeof this.state.ids === 'undefined',
page,
perPage,
referenceBasePath,
setPage: this.setPage,
setPerPage: this.setPerPage,
setSort: this.setSort,
total
})
}
}
ReferenceManyManyFieldController.defaultProps = {
perPage: 25,
sort: {field: 'id', order: 'DESC'}
}
//ReferenceManyManyFieldView
export const ReferenceManyManyFieldView = ({
children,
classes = {},
className,
currentSort,
data,
ids,
isLoading,
page,
pagination,
perPage,
reference,
referenceBasePath,
setPerPage,
setPage,
setSort,
total
}) => (
isLoading ?
<LinearProgress className={classes.progress} />
:
<Fragment>
{React.cloneElement(children, {
className,
resource: reference,
ids,
data,
basePath: referenceBasePath,
currentSort,
setSort,
total
})}
{pagination && React.cloneElement(pagination, {
page,
perPage,
setPage,
setPerPage,
total
})}
</Fragment>
);
//Assuming the question example, the presentation of many-to-many relationship would be something like
const UserShow = ({...props}) => (
<Show {...props}>
<TabbedShowLayout>
<Tab label='User Roles'>
<ReferenceManyManyField source='users' reference='roles' addLabel={false} pagination={<Pagination/>}>
<Datagrid>
<TextField source='name'/>
<TextField source='code'/>
</Datagrid>
</ReferenceManyManyField>
</Tab>
</TabbedShowLayout>
</Show>
)
//Used <TabbedShowLayout> because is what I use in my project, not sure if works under <Show> or <SimpleShowLayout>, but I think it work since I use it in other contexts
我认为实现可以改进并且与 React-Admin 更兼容。在其他参考字段中,数据获取存储在 react-redux 状态中,在此实现中不是。除了使应用程序无法离线工作的组件之外,该关系没有保存在任何地方,因为无法获取数据,甚至无法进行排序。