Last active
July 29, 2024 08:28
-
-
Save leibowitz/b94fc4eb2b8ab745267db99941757190 to your computer and use it in GitHub Desktop.
AdminBro Many to Many component for sequelizejs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { Database, Resource: SequelizeResource } = require('admin-bro-sequelizejs') | |
const { BaseRecord } = require('admin-bro') | |
const { Op } = require('sequelize') | |
class Resource extends SequelizeResource { | |
titleField() { | |
return this.decorate().titleProperty().name() | |
} | |
wrapObjects(sequelizeObjects){ | |
return sequelizeObjects.map(sequelizeObject => new BaseRecord(sequelizeObject.toJSON(), this)) | |
} | |
async findRelated(record, toResourceId, options = {}) { | |
const instance = this.getInstance(record) | |
const association = this.getAssociationsByResourceId(toResourceId)[0] | |
return await instance[association.accessors.get](options) | |
} | |
getAssociationsByResourceId(resourceId) { | |
return Object.values(this.SequelizeModel.associations).filter(association => association.target.name === resourceId) | |
} | |
getInstance(record) { | |
return new this.SequelizeModel(record.params, {isNewRecord: false}) | |
} | |
async saveRecords(record, resourceId, ids) { | |
const instance = this.getInstance(record) | |
const association = this.getAssociationsByResourceId(resourceId)[0] | |
await association.set(instance, ids) | |
} | |
primaryKeyField() { | |
return this.SequelizeModel.primaryKeyField | |
} | |
getManyProperties() { | |
return this.decorate().getProperties({where: 'edit'}).filter(p => p.type() === 'many').map(p => p.name()) | |
} | |
} | |
module.exports = { Database, Resource } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const AdminBro = require('admin-bro') | |
const Adapter = require('./adapter.js') | |
const { after: manyToManyAfterHook } = require('./manytomany.hook') | |
// Import your model | |
const db = require('./models'); | |
AdminBro.registerAdapter(Adapter) | |
const manyToManyComponent = { | |
type: 'many', | |
components: { | |
edit: AdminBro.bundle('./manytomany.edit.jsx'), | |
} | |
} | |
const manyToManyActionHooks = { | |
new: { | |
after: manyToManyAfterHook, | |
}, | |
edit: { | |
after: manyToManyAfterHook, | |
} | |
} | |
// Let's say you have two models, user and group | |
// Linked by a many-to-many relationship | |
// | |
// You could just add the "group" property to | |
// the User resource to edit the association | |
// with groups from the user edit page | |
const adminBro = new AdminBro({ | |
resources: [ | |
{ | |
resource: db.sequelize.models.user, | |
options: { | |
actions: manyToManyActionHooks, | |
editProperties: [ | |
'name', | |
'email', | |
'group', | |
], | |
properties: { | |
group: manyToManyComponent | |
} | |
} | |
}, | |
{ | |
resource: db.sequelize.models.group | |
} | |
] | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState } from 'react' | |
import { FormGroup, Label, ApiClient } from 'admin-bro' | |
import Select from 'react-select/lib/Async' | |
const getOptionsFromRecords = (records) => { | |
return records.map(r => ({value: r.id, label: r.title})) | |
} | |
const getItems = (record, name) => { | |
if (record.populated && record.populated[name]) { | |
return getOptionsFromRecords(record.populated[name]) | |
} | |
return [] | |
} | |
const ResourceSelection = (props) => { | |
const { onChange, name, selected: initialSelection, resourceId } = props | |
const [options, setOptions] = useState([]) | |
const [selected, setSelected] = useState(initialSelection) | |
const api = new ApiClient() | |
const loadOptions = async (inputValue) => { | |
const records = await api.searchRecords({ resourceId: resourceId, query: inputValue }) | |
const options = getOptionsFromRecords(records) | |
setOptions(options) | |
return options | |
} | |
const handleChange = (selectedOptions) => { | |
setSelected(selectedOptions) | |
onChange(name, selectedOptions.map(v => v.value)) | |
} | |
return ( | |
<Select isMulti defaultOptions loadOptions={loadOptions} value={selected} onChange={handleChange} /> | |
) | |
} | |
const ManyToManyEdit = (props) => { | |
const { property, record, onChange } = props; | |
const items = getItems(record, property.name) | |
return ( | |
<FormGroup> | |
<Label>{property.label}</Label> | |
<ResourceSelection onChange={onChange} name={property.name} resourceId={property.name} selected={items} /> | |
</FormGroup> | |
) | |
} | |
export default ManyToManyEdit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { unflatten } = require('flat') | |
const setResponseItems = async (context, response, toResourceId) => { | |
const { _admin, resource, record } = context | |
const toResource = _admin.findResource(toResourceId) | |
const options = {order: [toResource.titleField()]} | |
const throughItems = await resource.findRelated(record, toResourceId, options) | |
const items = toResource.wrapObjects(throughItems) | |
if (items.length !== 0) { | |
const primaryKeyField = toResource.primaryKeyField() | |
response.record.populated[toResourceId] = items | |
response.record.params[toResourceId] = items.map(v => v.params[primaryKeyField || 'id']) | |
} | |
} | |
const after = async (response, request, context) => { | |
if (request && request.method) { | |
const manyProperties = context.resource.getManyProperties() | |
if (context.action.name == 'edit' && request.method === 'get') { | |
// Load all linked data | |
await Promise.all(manyProperties.map(async (toResourceId) => { | |
await setResponseItems(context, response, toResourceId) | |
})) | |
} | |
const { record } = context | |
if (request.method === 'post' && record.isValid()) { | |
const params = unflatten(request.payload) | |
await Promise.all(manyProperties.map(async (toResourceId) => { | |
const ids = params[toResourceId] ? params[toResourceId].map(v => parseInt(v)) : [] | |
await context.resource.saveRecords(record, toResourceId, ids) | |
})) | |
} | |
} | |
return response | |
} | |
module.exports = { after } |
Most likely. In this case you just have to adapt the adapter.js to the underlying adapter
any way to do this in typeorm?
@DakshMiglani were you able to get it working with typeorm?
Most likely. In this case you just have to adapt the adapter.js to the underlying adapter
@leibowitz can you show some guide or some help for this ?
I got this error , can you help ?
React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined
any way to do this in typeorm?
I want to implement in typeorm for many to many
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
any way to do this in typeorm?