English
md.render.before
About 735 wordsAbout 2 min
2025-12-15
This hook is called before the child-object table is rendered. It allows extra actions before rendering, including but not limited to the following capabilities:
- Extend child-form buttons
- Customize child-table fields
- Modify child-table data
- Add custom areas to child tables
Parameters
| Parameter | Description | Type |
|---|---|---|
| Common parameters | See details | -- |
| objApiName | Child object apiName | String |
| recordType | Child object record type | String |
Return Value
| Parameter | Description | Type | Default |
|---|---|---|---|
| buttons | Common buttons at the top-right corner of the child-object table | Object | -- |
| operateBtns | Single-row operation buttons in the child-object table | Array | -- |
| batchButtons | Batch buttons at the top-left corner of the child-object table | Object | -- |
| columnRenders | Custom column rendering | Array | -- |
| columnWidthConfig | Custom column widths | Object | -- |
| hideFields | Hide specified fields | Object | -- |
| filterBatchEditFields | Disable batch editing for specified fields | Array | -- |
| fakeFields | Add virtual fields to the table | Array | -- |
| headerSlot | Custom header area | Array | -- |
| footerSlot | Custom footer area | Array | -- |
| tableFooterSlot | Custom bottom area of the child-object table | Object | -- |
| tableLeftSlot | Custom left-side area | Array | -- |
| titSlot | Custom operation area | Object | -- |
| resetFields | Reset field properties / readonly / required / label / options / type | Object | -- |
| datasErrorCallBack | Callback for data errors when submitting | Function | -- |
| parseData | Process child-object data before submission | Function | -- |
buttons / operateBtns / batchBtns
| Parameter | Description | Type | Default |
|---|---|---|---|
| add | Add custom buttons | Array | -- |
| del | Delete specified buttons | Array | -- |
| reset | Reset button names and behavior | Array | -- |
| retain | Retain only specified buttons | Array | -- |
resetFields
| Parameter | Description | Type | Default |
|---|---|---|---|
| is_readonly | Read-only status | Boolean | -- |
| is_required | Required status | Boolean | -- |
| type | Field type | String | -- |
| label | Label name | String | -- |
| options | Options, supported only for single-select and multi-select fields | Array | -- |
Basic Examples
Custom top-right table buttons
export default class Plugin {
apply() {
return [{
event: 'md.render.before',
functional: this.mdRenderBefore.bind(this)
}]
}
mdRenderBefore(context, plugin) {
if(context.objApiName === 'object_y6JkA__c') {
return Promise.resolve({
buttons: {
add: [{
action: '',
label: 'Add Some Data',
callBack(arg1, arg2) {
let {objApiName, recordType} = arg2;
let newContext = plugin.api.pluginServiceFactory({objApiName, recordType});
let datas = [{
objApiName,
recordType,
name: 'test'
}]
let newDatas = newContext.dataUpdater.add(datas);
let newDataIndexs = [];
newDatas.forEach(data => newDataIndexs.push(data.rowId));
newContext.triggerCalAndUIEvent({newDataIndexs}).then(rst => {
if(rst.statusCode) {
newContext.end(true);
} else {
newContext.end();
}
})
}
}],
del: ['Single_Add', 'product_id']
}
})
}
}
}Custom single-row operation buttons
export default class Plugin {
apply() {
return [{
event: 'md.render.before',
functional: this.mdRenderBefore.bind(this)
}]
}
mdRenderBefore(context, plugin) {
if(context.objApiName === 'object_y6JkA__c') {
return Promise.resolve({
operateBtns: [(trData) => {
if(trData.record_type === 'default__c') {
return {
add: [{
action: '',
label: 'Button test',
callBack(arg1, arg2) {
let {objApiName, recordType} = arg2;
let newContext = plugin.api.pluginServiceFactory({objApiName, recordType});
newContext.dataUpdater.updateData(objApiName, arg1.rowId, {name: 'xxxxx'});
newContext.triggerCalAndUIEvent({dataIndex: arg1.rowId}).then(rst => {
if(rst.statusCode) {
newContext.end(true);
} else {
newContext.end();
}
})
}
}]
}
}
}]
})
}
}
}Custom batch operation buttons
export default class Plugin {
apply() {
return [{
event: 'md.render.before',
functional: this.mdRenderBefore.bind(this)
}]
}
mdRenderBefore(context, plugin) {
if(context.objApiName === 'object_y6JkA__c')
return Promise.resolve({
batchButtons: {
add: [{
label: 'Total Amount of Selected Data',
callBack(arg1, arg2) {
let {objApiName, recordType} = arg2;
let newContext = plugin.api.pluginServiceFactory({objApiName, recordType});
let checkedDatas = newContext.dataGetter.getCheckedDatas(objApiName, recordType);
let total = 0;
checkedDatas.forEach(data => {total += (data.field_dg9xI__c || 0) * 1})
alert(`The total amount of currently selected data is ${total}`)
}
}]
}
})
}
}Custom column widths and field reset
export default class Plugin {
apply() {
return [{
event: 'md.render.before',
functional: this.mdRenderBefore.bind(this)
}]
}
mdRenderBefore(context, plugin) {
return {
columnWidthConfig: {
name: 200,
field_v1A0B__c: 300
},
resetFields: {
name: {
is_readonly: true | false,
is_required: true | false,
type: 'select_one',
label: 'xxxx'
}
}
}
}
}Hide fields and process data before submission
export default class Plugin {
apply() {
return [{
event: 'md.render.before',
functional: this.mdRenderBefore.bind(this)
}]
}
mdRenderBefore(context, plugin) {
return {
hideFields: {common:['name', 'price']},
parseData: [(dataList) => {
return dataList
}]
}
}
}Notes
1. This hook is triggered before each child-object table is rendered
Each record type under each child object renders its own table, and this hook runs before each one. Use objApiName and recordType to distinguish which table is being processed.
2. retain has higher priority than add
When customizing buttons, if retain is provided, buttons listed in add will not be created.
Appendix
Preset Buttons
Table Buttons
| Button | action |
|---|---|
| Add One Row | Single_Add |
| Import from Local Excel | Import_Excel |
Single-row operation buttons
| Button | action |
|---|---|
| Delete | delRowHandle |
| Copy | copyRowHandle |
| Tile | tileHandle |
| Insert | insertHandle |
| Drag | dragHandle |
Batch Buttons
| Button | action |
|---|---|
| Batch Edit | tableBatchEditHandle |
| Delete | delSelectedHandle |
| Copy | copySelectedHandle |
FAQ
Q: Why does plugin.api.pluginServiceFactory throw an error saying the method cannot be found?
A: Because this is a middleware plugin written in a vcrm project, the interaction parameter positions need to be swapped: mdRenderBefore(context,plugin) -> mdRenderBefore(plugin, context).
Q: Why does the button callBack need to create a newContext?
A: Because when the button is executed, the previous context has already been destroyed, so a new one must be created.
