将多个列表视图添加到同一资源 - Admin-On-Rest
Adding multiple list views to same resource - Admin-On-Rest
我有一个故事资源,我想根据故事状态以不同方式为同一用户显示
状态为 NEW 的故事应与 List1 一起显示
status = APPROVED 等的故事需要显示为 List2(我需要显示故事的不同属性)
如何使用 Admin-On-Rest 实现此目的?
两次添加相同的资源(如下所示)并为两者分配不同的列表视图只会导致显示第一个而忽略第二个
<Resource name="tales" list={EditorAssignedList} edit={EditTale} options={{ label: 'Assigned Tales' }}/>
<Resource name="tales" list={EditorTaleTrack} options={{ label: 'Your Tales' }}/>
记录了以下错误。
flattenChildren(...): Encountered two children with the same key, `1:$tales`. Child keys must be unique;
关于如何将唯一键注入资源的任何策略。
只保留一个资源。创建一个包含 2 个条目的自定义菜单,将您的过滤器传递给 url 参数。
然后在 TalesList 组件中,根据您的参数显示正确的版本组件
在@Gildas 的帮助下解决了问题
解决了这个问题
1) 创建自定义菜单组件
const linkData = {
pathname: "/tales",
hash: "trackTales"
}
export default ({ resources, onMenuTap, logout }) => {
return (
<div>
<MenuItem containerElement={<Link to="/tales" />} primaryText="Tales For Edit" onTouchTap={onMenuTap} />
<MenuItem containerElement={<Link to={ linkData } />} primaryText="Track Tales" onTouchTap={onMenuTap} />
{logout}
</div>
)
}
React Router 的 Link 组件接受对象作为参数。这些作为道具传递给下游组件。
export const EditorAssignedList = (props) => {
return taleViewJuggler(props)
}
juggler 函数读取道具并根据道具创建自定义视图。 Link 组件正在将数据传递给道具中的 'location' 键。
const taleViewJuggler = (props) => {
let viewName = props.location.hash
let component;
switch (viewName) {
case "case1":
component = (
<ListView1 />
)
break;
case "#case2":
component = ( < ListView2 /> )
break;
}
return component
}
如果您还想通过不同的列表菜单向同一路由添加 CRUD 功能,那么上面的答案并不是那么有用。如果您有 2 个带有 CRUD 组件的列表视图 List1 和 List2。从 List2 输入编辑(例如)并点击保存,您将被重定向到 List1
更广泛的解决方案是为您的 REST 客户端创建自定义包装器。
灵感来自下方。
https://marmelab.com/admin-on-rest/RestClients.html#decorating-your-rest-client-example-of-file-upload
我的情况是这样的。
我在 App.js 中创建了一个虚拟资源 'trackTale'。在 restWrapper.js
const RESTWrapper = requestHandler => (type, resource, params) => {
if (type === 'GET_LIST' && resource === 'trackTale') {
const page = params.pagination.page
const perPage = params.pagination.perPage
const {field, order} = params.sort
const query = {}
query['where'] = {...params.filter}
if (field) {query['order'] = [field + ' ' + order]}
if (perPage > 0) {
query['limit'] = perPage;
if (page >= 0) {
query['skip'] = (page - 1) * perPage
}
}
//Key part here hardcoding the tales route to trackTale
const url = config.host + '/' + 'tales?' + queryParameters({filter: JSON.stringify(query)})
const options = {};
options.user = {authenticated: true}
options.user.token = localStorage.getItem('token');
options.method = 'GET';
return fetchUtils.fetchJson(url, options)
.then((response) => {
const {headers, json} = response;
//admin on rest needs the {data} key
return {data: json,
total: parseInt(headers.get('x-total-count').split('/').pop(), 10)}
})
}
}
//below function is very very specific to how loopback.js expects to recieve REST queries. Please customize it for your API needs
const queryParameters = data => Object.keys(data)
.map(key => [key, data[key]].map(encodeURIComponent).join('='))
.join('&');
这适用于所有情况。如果您的不同路线没有 CRUD,仍然可以创建自定义菜单。
我有一个故事资源,我想根据故事状态以不同方式为同一用户显示
状态为 NEW 的故事应与 List1 一起显示
status = APPROVED 等的故事需要显示为 List2(我需要显示故事的不同属性)
如何使用 Admin-On-Rest 实现此目的?
两次添加相同的资源(如下所示)并为两者分配不同的列表视图只会导致显示第一个而忽略第二个
<Resource name="tales" list={EditorAssignedList} edit={EditTale} options={{ label: 'Assigned Tales' }}/>
<Resource name="tales" list={EditorTaleTrack} options={{ label: 'Your Tales' }}/>
记录了以下错误。
flattenChildren(...): Encountered two children with the same key, `1:$tales`. Child keys must be unique;
关于如何将唯一键注入资源的任何策略。
只保留一个资源。创建一个包含 2 个条目的自定义菜单,将您的过滤器传递给 url 参数。
然后在 TalesList 组件中,根据您的参数显示正确的版本组件
在@Gildas 的帮助下解决了问题
解决了这个问题1) 创建自定义菜单组件
const linkData = {
pathname: "/tales",
hash: "trackTales"
}
export default ({ resources, onMenuTap, logout }) => {
return (
<div>
<MenuItem containerElement={<Link to="/tales" />} primaryText="Tales For Edit" onTouchTap={onMenuTap} />
<MenuItem containerElement={<Link to={ linkData } />} primaryText="Track Tales" onTouchTap={onMenuTap} />
{logout}
</div>
)
}
React Router 的 Link 组件接受对象作为参数。这些作为道具传递给下游组件。
export const EditorAssignedList = (props) => {
return taleViewJuggler(props)
}
juggler 函数读取道具并根据道具创建自定义视图。 Link 组件正在将数据传递给道具中的 'location' 键。
const taleViewJuggler = (props) => {
let viewName = props.location.hash
let component;
switch (viewName) {
case "case1":
component = (
<ListView1 />
)
break;
case "#case2":
component = ( < ListView2 /> )
break;
}
return component
}
如果您还想通过不同的列表菜单向同一路由添加 CRUD 功能,那么上面的答案并不是那么有用。如果您有 2 个带有 CRUD 组件的列表视图 List1 和 List2。从 List2 输入编辑(例如)并点击保存,您将被重定向到 List1
更广泛的解决方案是为您的 REST 客户端创建自定义包装器。 灵感来自下方。 https://marmelab.com/admin-on-rest/RestClients.html#decorating-your-rest-client-example-of-file-upload
我的情况是这样的。
我在 App.js 中创建了一个虚拟资源 'trackTale'。在 restWrapper.js
const RESTWrapper = requestHandler => (type, resource, params) => {
if (type === 'GET_LIST' && resource === 'trackTale') {
const page = params.pagination.page
const perPage = params.pagination.perPage
const {field, order} = params.sort
const query = {}
query['where'] = {...params.filter}
if (field) {query['order'] = [field + ' ' + order]}
if (perPage > 0) {
query['limit'] = perPage;
if (page >= 0) {
query['skip'] = (page - 1) * perPage
}
}
//Key part here hardcoding the tales route to trackTale
const url = config.host + '/' + 'tales?' + queryParameters({filter: JSON.stringify(query)})
const options = {};
options.user = {authenticated: true}
options.user.token = localStorage.getItem('token');
options.method = 'GET';
return fetchUtils.fetchJson(url, options)
.then((response) => {
const {headers, json} = response;
//admin on rest needs the {data} key
return {data: json,
total: parseInt(headers.get('x-total-count').split('/').pop(), 10)}
})
}
}
//below function is very very specific to how loopback.js expects to recieve REST queries. Please customize it for your API needs
const queryParameters = data => Object.keys(data)
.map(key => [key, data[key]].map(encodeURIComponent).join('='))
.join('&');
这适用于所有情况。如果您的不同路线没有 CRUD,仍然可以创建自定义菜单。