This way of setting Multiselectlookup(Lookup that allows Multiple value selections) can be used on any module (SSRS, Service, Batch or simple server/client based service class) utilizing SysOperation framework.
Code design would be as follows:-
1. Data contract class: (just showing relevant parameters, there can be other parameters too). Just to note, container type parameter can also be used in place of List.
[DataContract, SysOperationContractProcessing(ClassStr(TestExpImpUIBuilder))] class TESTEXPDC extends SysOperationServiceBaseDataContract { List department; [DataMemberAttribute, SysOperationLabel("@SYS850"), AifCollectionTypeAttribute('return', Types::String)] public List parmDepartment(List _department = department) { department = _department; return department; } }2. UI Builder Class: This is most important to build a multi select lookup.
class TestExpImpUIBuilder extends SysOperationAutomaticUIBuilder { DialogField dialogDepartment; SysLookupMultiSelectCtrl ctrlDepartment; //Override this to add dialog field protected DialogField addDialogField(IdentifierName _methodName, Object _dataContract = this.dataContractObject()) { DialogField dialogField;; switch (_methodName) { case methodStr(TESTEXPDC, parmDepartment): dialogDepartment = this.dialog().addField( extendedTypeStr(Description), "@SYS850"); dialogField = dialogDepartment; break; default: dialogField = super(_methodName, _dataContract); } return dialogField; } //Override this public void postRun() { super(); if (this.dataContractObject() is TESTEXPDC) //if is optional, in my case i was using single UI builder for multiple tasks so required { this.lookupDepartment(); } } //to populate lookup protected void lookupDepartment() { TableId multiSelectTableNum = tableNum(EcoResCategory); Query query = new Query(); QueryBuildDataSource qbds = query.addDataSource(multiSelectTableNum); EcoResCategoryHierarchyRole categoryRole; select firstonly categoryRole where categoryRole.NamedCategoryHierarchyRole == EcoResCategoryNamedHierarchyRole::RetailChannelNavigation; qbds.addRange(fieldNum(EcoResCategory, Level)).value(queryValue(3)); qbds.addRange(fieldNum(EcoResCategory, CategoryHierarchy)).value(queryValue(categoryRole.CategoryHierarchy)); qbds.addSelectionField(fieldNum(EcoResCategory, Code)); //needed for field display in lookup qbds.addSelectionField(fieldNum(EcoResCategory, Name)); container selectedFields = [multiSelectTableNum, fieldNum(EcoResCategory, Code)]; //irrelevant for lookup but value needed for further use in method calling ctrlDepartment = SysLookupMultiSelectCtrl::constructWithQuery(this.dialog().dialogForm().formRun(), dialogDepartment.control(), query, false, selectedFields); } public void getFromDialog() { super(); //keep in mind, multiselect actually brings Recid and works like reference lookup if (this.dataContractObject() is TESTEXPDC) { List listDepartment = new List(Types::String); container conDeptt = ctrlDepartment.get(); Counter conCount = 1; Counter conDepLength = conLen(conDeptt); while (conCount //use sign for less and equal here// conDepLength) { listDepartment.addEnd(EcoResCategory::find(conPeek(conDeptt, conCount)).Code); conCount++; } this.dataContractObject().parmDepartment(listDepartment); //whole list code not needed in case you are using container type parameter //this.dataContractObject().parmDepartment(ctrlDepartment.get()); } } }
3. Service class : Its optional and purely based upon requirement. No lookup code is needed here, just needed for execution type and Contract mapping to further call Task based class.
4. Task class: actual iteration of selected multiselect values would be done here.
class TESTEXPTask { List listDepartment; public static TESTEXPTask construct() { return new TESTEXPTask(); } public void run(TESTEXPDC _contract) { this.processDataforExport(); } protected void processDataforExport() { try { ttsbegin; // any code execution //Line Creation this.createLinesPerDepartment(); ttscommit; } catch (Exception::Error) { exceptionTextFallThrough(); } } protected void createLinesPerDepartment() { if (listDepartment && !listDepartment.empty()) { ListEnumerator listEnumerator; listEnumerator = listDepartment.getEnumerator(); while (listEnumerator.moveNext()) { this.methodName(listEnumerator.current()); } } else { //call execution code this.methodeName(); } }
and you are done. Just squeeze in your other code like additional parameters and business code and voila!!!
Keep in mind, UI builder class in anyways would be used in similar old manner (overridelookup) for conventional field lookups.
See you in next blog. Happy Daxing (Now D365ing)!!! Please drop a feedback in case of any issues or any other concerns.