案例展示NetSuite-UI界面中创建动态下拉列表
在NetSuite的主界面或列表中,创建动态下拉子列表,无缝嵌入到商业应用流程中
有什么用
学会如何在NetSuite UI界面中创建动态下拉列表
- 在主界面(Custom Body Field)中创建动态下拉列表字段
- 在列表行(Custom Line Item Field)中创建动态下拉列表字段
在现实业务流程中,这样的动态下拉列表应用十分广泛;而NetSuite自带的自定义字段的动态下来过滤功能十分有限。很多的应用场景无法满足,这里就不一一展开说明,懂的都懂。
需求
本案例:要在下拉列表中,将Replacement Vendor字段的内容,动态地根据Replacement Item字段的内容而决定。就是根据Item货品的供应商列表来过滤当前自定义记录界面中的Replacement Vendor字段的下拉内容,从而达到Replacement Vendor字段的下拉列表不展示系统中所有的Vendor,而是限定于特定Item货品(当前行中的货品字段)的供应商列表(这个使用其实也同时可以展示该供应商的此货品采购价)
怎么用
- 创建一个系统中的自定义字段
- 如果是把动态列表放在主界面,那么该‘容器’字段也可以使用UserEvent的script生成出这个一个动态的自定义字段。
- 如果是把动态列表放在主界面下的列表中(比如:销售订单的货品列表中),那么就必须要新建一个自定义列字段;
- 而假如是要把动态列表放在主界面下的列表中(比如:主从关系的自定义记录类型Custom Record Type),那么就是新建一个自定字段在在子记录类型中(这个子自定义记录类型在UI中会变成一个列表sublist)
- 新建一个Library脚本文件Script(详见下方的PRI_DropDown_lib.js文件全部内容展示)
- 创建一个User Event Script记录
- 用来新建初始化字段(可选,根据实际需要)
- 用来初始化字段的HTML内容;这个初始化内容是根据列表中当前的货品来决定的,UI的编辑状态用户是可以随时在界面中切换货品字段内容,从而需要重新初始化该HTML内容(详见实现在下方的Client Script中)
- 创建一个Client Script记录
- 在列表的line init事件中,初始化当前行的Vendor列表(根据当前的货品字段内容)
- 在用户切换了Item货品字段内容后,重新刷新初始化当前行的Vendor列表
相关内容
实现方法
Create Custom Field
新建自定义字段的定义案例:
说明:这个字段的TYPE,用Free Form Text也是一样的。
User Event
to initial the UI to put-in/inject the dropdown HTML
var intDefProject = currentRecord.getValue('custbody_pri_prj');
var clsProjectDropDown = new CLLIB.PROJECTDROPDOWN(intEntityId,
'');
// Set entity projects dropdown options HTML
var strInitSelectHtml = clsProjectDropDown.initOptionHtml(
intDefProject, scriptContext.type);
objFldProject.defaultValue = "<div class=\"uir-field-wrapper\" data-field-type=\"text\"><span id=\"projectdropdown_fs_lbl_uir_label\" class=\"smallgraytextnolink uir-label \"><span id=\"projectdropdown_fs_lbl\" class=\"labelSpanEdit smallgraytextnolink\" style=\"\">"
+ "<a tabindex=\"-1\" title=\"What's this?\" href='javascript:void(\"help\")' style=\"cursor:help\" class=\"smallgraytextnolink\" onmouseover=\"this.className='smallgraytext'; return true;\" onmouseout=\"this.className='smallgraytextnolink'; \">"
+ strPrjLbl
+ "</a>"
+ "</span></span><span class=\"uir-field\">"
+ strInitSelectHtml + "</span>" + "</div>";
上面这个方案是直接注入到系统的自定义字段中(在NetSuite系统的自定义字段中定义的)
或者使用全新的Script新增的动态的html字段
var objFldProject = objForm.addField({
id: 'custpage_pri_prj',
type: ui.FieldType.INLINEHTML,
label: strPrjLbl
});
objForm.insertField({
field: objFldProject,
nextfield: 'custbody_pri_co',// 'job'
});
objFldProject.defaultValue = "<div class=\"uir-field-wrapper\" data-field-type=\"text\"><span id=\"projectdropdown_fs_lbl_uir_label\" class=\"smallgraytextnolink uir-label \"><span id=\"projectdropdown_fs_lbl\" class=\"labelSpanEdit smallgraytextnolink\" style=\"\">"
+ "<a tabindex=\"-1\" title=\"What's this?\" href='javascript:void(\"help\")' style=\"cursor:help\" class=\"smallgraytextnolink\" onmouseover=\"this.className='smallgraytext'; return true;\" onmouseout=\"this.className='smallgraytextnolink'; \"> "
+ strPrjLbl
+ "</a>"
+ "</span></span><span class=\"uir-field\">"
+ "<select id=\"custpage_projectdropdown\" name=\"custpage_projectdropdown\" autocomplete=\"off\" lineindex=\"1\" class=\"input uir-custom-field\">"
+ "<option value=\"\"> </option>"
+ "</select>"
+ "</span>" + "</div>" + "";
Client Script dynamic behaviors
pageInit
可选,仅用于使用主界面的系统自带自定义字段来‘加载’动态列表的情况下
/**
* Function to be executed after page is initialized.
*
* @param {Object}
* scriptContext
* @param {Record}
* scriptContext.currentRecord - Current form record
* @param {string}
* scriptContext.mode - The mode in which the record is
* being accessed (create, copy, or edit)
*
* @since 2015.2
*/
function pageInit(scriptContext) {
...
// Bind select option change event
var clsProjectDropDown = new CLLIB.PROJECTDROPDOWN(intEntityId,
objDropdownDom, {'currentRecord': currentRecord});
clsProjectDropDown.bindSelectEvent();
...
}
fieldChanged
function fieldChanged(scriptContext) {
switch (scriptContext.fieldId) {
case ....
// Bind select option change event
var clsProjectDropDown = new CLLIB.PROJECTDROPDOWN(
intEntityId, objDropdownDom, {'currentRecord': currentRecord});
if (!intEntityId) {
clsProjectDropDown.clearSelectOptions();
return true;
}
// Draw entity project dropdown options
clsProjectDropDown.drawDOM();
break;
}
}
pri_RetMgr_vendor_refreshSelectOption
function pri_RetMgr_vendor_refreshSelectOption(currentRecord) {
var lnIdx = currentRecord.getCurrentSublistIndex({
sublistId: strSublistId_replc
});
var intItemId = currentRecord.getCurrentSublistValue(strSublistId_replc, RETMGRLIB.REC_RETURNMGR_REPLC_LINE.ITEM);
var intVendorId_cur = currentRecord.getCurrentSublistValue(strSublistId_replc, RETMGRLIB.REC_RETURNMGR_REPLC_LINE.VENDOR);
try {
var objDropdownDom = document.getElementById('recmachcustrecord_pri_return_mgr_replc_parent_custrecord_pri_return_mgr_replc_selvnd_fs');
// Bind select option change event
var clsVendorDropDown = new DDLIB.DROPDOWN(intItemId, objDropdownDom, {'currentRecord': currentRecord});
jQuery('#recmachcustrecord_pri_return_mgr_replc_parent_custrecord_pri_return_mgr_replc_selvnd_fs').html(clsVendorDropDown.initOptionHtml(intVendorId_cur));
clsVendorDropDown.bindSelectEvent();
} catch (ex) {
console.log(ex);
}
// Special case: After you change the item from Item A to Item B, the original vendor value should/might be cleared(since that vendor is not in new Item B’s vendor list)
if (intVendorId_cur) {
var bolVendorAvailable = false;
var arrProjectResObj = clsVendorDropDown.arrProjectResObj;
for (var i = 0; arrProjectResObj && i < arrProjectResObj.length; i++) {
if (intVendorId_cur == arrProjectResObj[i].ID){
bolVendorAvailable = true;
break;
}
}
if (bolVendorAvailable === false)
currentRecord.setCurrentSublistValue({
sublistId: strSublistId_replc,
fieldId: RETMGRLIB.REC_RETURNMGR_REPLC_LINE.VENDOR,
value: ''
});
}
}
从这个函数可以得知这个自定义的动态下拉字段,初始化的值也是会被加载的,非常顺滑。
Library - PRI_DropDown_lib.js
used in both Client and UserEvent/Server side. 核心公共函数库
//------------------------------------------------------------------
//Developer: Carl
//Description: Need a dynamic Drop Down on the record/transaction of vendor list.
// Thus, the drop down on record/transaction should filter for item that are related.
//------------------------------------------------------------------
/**
* @NApiVersion 2.x
* @NModuleScope Public
*/
define(
['N/error', 'N/record', 'N/runtime', 'N/search', './PRI_RM_ReturnManager_lib'],
/**
* @param {error}
* error
* @param {record}
* record
* @param {runtime}
* runtime
* @param {search}
* search
* @param {dialog}
* dialog
*/
function (error, record, runtime, search, RETMGRLIB) {
// ---------------------- Drop Down Class-----------------
function DROPDOWN(intItemId, objDropdownDom, options) {
this.intItemId = intItemId;
this.objDropdownDom = objDropdownDom;
if (intItemId) {
this.arrProjectResObj = this.lookupVendorRecords();
}
this.strInitSelectFld = "<select id=\"custpage_projectdropdown\" name=\"custpage_projectdropdown\" autocomplete=\"off\" lineindex=\"1\" class=\"input uir-custom-field\">"
+ "<option value=\"\"> </option>"
+ 'REPLACE_OPTIONAL_HTML' + "</select>";
if (options && typeof (options.currentRecord) != 'undefined') {
this.currentRecord = options.currentRecord;
}
}
/**
* lookup Vendor Records
*
* @param {String}
* strLookupKeyWord Lookup Keyword
* @returns {Object} objOrderStatusSublist
*/
DROPDOWN.prototype.lookupVendorRecords = function (strLookupKeyWord) {
var intItemId = this.intItemId;
if (!intItemId)
return [];
var arrProjectRes = [];
var objFilters = [['isinactive', 'is', 'F']];
// if (intItemId) {
// objFilters.push('and');
// objFilters.push([ RETMGRLIB.REC_PRIPROJECT.CUSTOMER, 'anyof',
// [ intItemId ] ]);
// }
objFilters.push('and');
objFilters.push(['internalid', 'anyof', [intItemId]]);
var objColumns = ['itemid', 'displayname', 'othervendor', 'cost'];
objColumns.push({name: "internalid", join: "vendor"});
var objRecordRes = search.create(
{
type: 'item',
filters: objFilters,
columns: objColumns
}).run().getRange({
start: 0,
end: 1000
});
for (var i = 0; objRecordRes && i < objRecordRes.length; i++) {
arrProjectRes.push({
ID: objRecordRes[i].getValue({
name: "internalid",
join: "vendor"
}),
// NAME: objRecordRes[i].getValue('itemid'),
COST: objRecordRes[i].getValue('cost'),
VALUE: objRecordRes[i].getText('othervendor')
});
}
return arrProjectRes;
};
/**
* Dynamically clear select options
*
* @returns {Boolean}
*/
DROPDOWN.prototype.clearSelectOptions = function () {
jQuery("#custpage_projectdropdown").replaceWith(
this.strInitSelectFld.replace('REPLACE_OPTIONAL_HTML',
''));
return true;
};
/**
* Draw select option DOM element
*/
DROPDOWN.prototype.drawDOM = function () {
var objDropdownDom = this.objDropdownDom;
var arrProjectResObj = this.arrProjectResObj ? this.arrProjectResObj : this.lookupVendorRecords();
var strOptionHtml = '';
for (var idx = 0; idx < arrProjectResObj.length; idx++) {
strOptionHtml += '<option value="'
+ arrProjectResObj[idx].ID
+ '" data-status='
+ arrProjectResObj[idx].COST
+ ' title="'
+ JSON.stringify(arrProjectResObj[idx]).replace(
/\"/g, "'") + '">'
+ arrProjectResObj[idx].VALUE + '</option>';
}
var strInitSelectFld = this.strInitSelectFld.replace(
'REPLACE_OPTIONAL_HTML', strOptionHtml);
jQuery("#custpage_projectdropdown").replaceWith(
strInitSelectFld);
this.bindSelectEvent();
};
/**
* Bind Selection Operation <br>
* Note: Only used in Client Side
*/
DROPDOWN.prototype.bindSelectEvent = function () {
jQuery(document).ready(
function () {
jQuery("#custpage_projectdropdown").change(
function () {
// alert(jQuery(this).val());
nlapiSetCurrentLineItemValue('recmachcustrecord_pri_return_mgr_replc_parent', RETMGRLIB.REC_RETURNMGR_REPLC_LINE.VENDOR,
jQuery(this).val());
// var strStatus = jQuery(this).find(
// ":selected").data("status");
});
});
};
/**
* Initial Option HTML, can use in server side
*
* @param {integer}
* intDefSelectVal Default Contact record type Id
* @param {string}
* strUserEventType User Event Type
* @returns {string}
*/
DROPDOWN.prototype.initOptionHtml = function (
intDefSelectVal, strUserEventType) {
// if (!this.intItemId)
// return '';
var arrProjectResObj = this.arrProjectResObj ? this.arrProjectResObj : this.lookupVendorRecords();
var strOptionHtml = '';
for (var idx = 0; idx < arrProjectResObj.length; idx++) {
if (intDefSelectVal
&& arrProjectResObj[idx].ID == intDefSelectVal)
strOptionHtml += '<option selected="selected" value="'
+ arrProjectResObj[idx].ID
+ '" data-status='
+ arrProjectResObj[idx].COST
+ ' title="'
+ JSON.stringify(arrProjectResObj[idx])
.replace(/\"/g, "'") + '">'
+ arrProjectResObj[idx].VALUE + '</option>';
else
strOptionHtml += '<option value="'
+ arrProjectResObj[idx].ID
+ '" data-status='
+ arrProjectResObj[idx].COST
+ ' title="'
+ JSON.stringify(arrProjectResObj[idx])
.replace(/\"/g, "'") + '">'
+ arrProjectResObj[idx].VALUE + '</option>';
}
var strInitSelectFld = this.strInitSelectFld;
if (strUserEventType == 'view')
strInitSelectFld = strInitSelectFld.replace(
'<select id=\"custpage_projectdropdown\"',
"<select id=\"custpage_projectdropdown\" disabled");
strInitSelectFld = strInitSelectFld.replace(
'REPLACE_OPTIONAL_HTML', strOptionHtml);
return strInitSelectFld;
};
return {
DROPDOWN: DROPDOWN
};
});
- bindSelectEvent,当完成了HTML展示动态更新的下拉子列表后,绑定新的点击事件;当用户选取新的列表选项后,我们将值传递给另一个系统的字段(或者其他的商业逻辑定义)
衍生阅读
来吧,约个时间当面谈,我很乐意效劳,有什么NetSuite系统定制开发中的疑难杂症我可以帮忙的吗?
随手记录 - 如何下载网页中视频
Firefox插件名称和版本信息