@sapui5/sap.fe.core 1.108.2 → 1.108.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/package.json +1 -1
  2. package/src/sap/fe/core/.library +1 -1
  3. package/src/sap/fe/core/ActionRuntime.js +2 -2
  4. package/src/sap/fe/core/ActionRuntime.ts +1 -1
  5. package/src/sap/fe/core/CommonUtils.js +7 -3
  6. package/src/sap/fe/core/CommonUtils.ts +2 -2
  7. package/src/sap/fe/core/TemplateModel.js +16 -2
  8. package/src/sap/fe/core/TemplateModel.ts +15 -1
  9. package/src/sap/fe/core/controllerextensions/EditFlow.js +2 -1
  10. package/src/sap/fe/core/controllerextensions/EditFlow.ts +1 -0
  11. package/src/sap/fe/core/controllerextensions/editFlow/operations.js +386 -357
  12. package/src/sap/fe/core/controllerextensions/editFlow/operations.ts +260 -224
  13. package/src/sap/fe/core/controls/ActionParameterDialog.fragment.xml +1 -0
  14. package/src/sap/fe/core/converters/controls/Common/Action.js +4 -2
  15. package/src/sap/fe/core/converters/controls/Common/Action.ts +1 -1
  16. package/src/sap/fe/core/converters/controls/Common/Table.js +7 -10
  17. package/src/sap/fe/core/converters/controls/Common/Table.ts +6 -6
  18. package/src/sap/fe/core/formatters/ValueFormatter.js +5 -3
  19. package/src/sap/fe/core/formatters/ValueFormatter.ts +17 -7
  20. package/src/sap/fe/core/helpers/BindingToolkit.js +2 -2
  21. package/src/sap/fe/core/helpers/BindingToolkit.ts +1 -1
  22. package/src/sap/fe/core/helpers/ModelHelper.js +2 -2
  23. package/src/sap/fe/core/helpers/ModelHelper.ts +1 -1
  24. package/src/sap/fe/core/library.js +1 -1
  25. package/src/sap/fe/core/messagebundle_ar.properties +18 -18
  26. package/src/sap/fe/core/messagebundle_bg.properties +17 -17
  27. package/src/sap/fe/core/messagebundle_ca.properties +18 -18
  28. package/src/sap/fe/core/messagebundle_cs.properties +18 -18
  29. package/src/sap/fe/core/messagebundle_cy.properties +18 -18
  30. package/src/sap/fe/core/messagebundle_da.properties +18 -18
  31. package/src/sap/fe/core/messagebundle_el.properties +17 -17
  32. package/src/sap/fe/core/messagebundle_en_GB.properties +18 -18
  33. package/src/sap/fe/core/messagebundle_es.properties +9 -9
  34. package/src/sap/fe/core/messagebundle_es_MX.properties +17 -17
  35. package/src/sap/fe/core/messagebundle_et.properties +18 -18
  36. package/src/sap/fe/core/messagebundle_fi.properties +18 -18
  37. package/src/sap/fe/core/messagebundle_fr.properties +17 -17
  38. package/src/sap/fe/core/messagebundle_fr_CA.properties +18 -18
  39. package/src/sap/fe/core/messagebundle_hi.properties +18 -18
  40. package/src/sap/fe/core/messagebundle_hr.properties +18 -18
  41. package/src/sap/fe/core/messagebundle_hu.properties +17 -17
  42. package/src/sap/fe/core/messagebundle_id.properties +18 -18
  43. package/src/sap/fe/core/messagebundle_it.properties +17 -17
  44. package/src/sap/fe/core/messagebundle_iw.properties +18 -18
  45. package/src/sap/fe/core/messagebundle_ja.properties +21 -21
  46. package/src/sap/fe/core/messagebundle_kk.properties +18 -18
  47. package/src/sap/fe/core/messagebundle_ko.properties +18 -18
  48. package/src/sap/fe/core/messagebundle_lt.properties +17 -17
  49. package/src/sap/fe/core/messagebundle_lv.properties +18 -18
  50. package/src/sap/fe/core/messagebundle_ms.properties +18 -18
  51. package/src/sap/fe/core/messagebundle_nl.properties +18 -18
  52. package/src/sap/fe/core/messagebundle_no.properties +17 -17
  53. package/src/sap/fe/core/messagebundle_pl.properties +17 -17
  54. package/src/sap/fe/core/messagebundle_pt.properties +18 -18
  55. package/src/sap/fe/core/messagebundle_pt_PT.properties +18 -18
  56. package/src/sap/fe/core/messagebundle_ro.properties +17 -17
  57. package/src/sap/fe/core/messagebundle_ru.properties +18 -18
  58. package/src/sap/fe/core/messagebundle_sh.properties +17 -17
  59. package/src/sap/fe/core/messagebundle_sk.properties +17 -17
  60. package/src/sap/fe/core/messagebundle_sl.properties +18 -18
  61. package/src/sap/fe/core/messagebundle_sv.properties +17 -17
  62. package/src/sap/fe/core/messagebundle_th.properties +17 -17
  63. package/src/sap/fe/core/messagebundle_tr.properties +18 -18
  64. package/src/sap/fe/core/messagebundle_uk.properties +18 -18
  65. package/src/sap/fe/core/messagebundle_vi.properties +18 -18
  66. package/src/sap/fe/core/messagebundle_zh_CN.properties +17 -17
  67. package/src/sap/fe/core/messagebundle_zh_TW.properties +17 -17
  68. package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +28 -53
  69. package/src/sap/fe/core/services/TemplatedViewServiceFactory.ts +36 -67
  70. package/src/sap/fe/core/templating/PropertyHelper.js +23 -2
  71. package/src/sap/fe/core/templating/PropertyHelper.ts +17 -0
  72. package/src/sap/fe/core/templating/SemanticObjectHelper.js +56 -0
  73. package/src/sap/fe/core/templating/SemanticObjectHelper.ts +34 -0
@@ -9,7 +9,9 @@ import { generate } from "sap/fe/core/helpers/StableIdHelper";
9
9
  import FELibrary from "sap/fe/core/library";
10
10
  import Button from "sap/m/Button";
11
11
  import Dialog from "sap/m/Dialog";
12
+ import type Label from "sap/m/Label";
12
13
  import MessageBox from "sap/m/MessageBox";
14
+ import type Event from "sap/ui/base/Event";
13
15
  import type Control from "sap/ui/core/Control";
14
16
  import Core from "sap/ui/core/Core";
15
17
  import Fragment from "sap/ui/core/Fragment";
@@ -17,9 +19,14 @@ import { MessageType } from "sap/ui/core/library";
17
19
  import Message from "sap/ui/core/message/Message";
18
20
  import XMLPreprocessor from "sap/ui/core/util/XMLPreprocessor";
19
21
  import XMLTemplateProcessor from "sap/ui/core/XMLTemplateProcessor";
22
+ import type Field from "sap/ui/mdc/Field";
23
+ import type MultiValueFieldItem from "sap/ui/mdc/field/MultiValueFieldItem";
24
+ import type MultiValueField from "sap/ui/mdc/MultiValueField";
25
+ import type Context from "sap/ui/model/Context";
20
26
  import JSONModel from "sap/ui/model/json/JSONModel";
27
+ import type AppComponent from "../../AppComponent";
21
28
  import operationsHelper from "../../operationsHelper";
22
- import MessageHandler from "../MessageHandler";
29
+ import type MessageHandler from "../MessageHandler";
23
30
 
24
31
  const Constants = FELibrary.Constants,
25
32
  InvocationGrouping = FELibrary.InvocationGrouping;
@@ -51,7 +58,7 @@ const Action = (MessageBox as any).Action;
51
58
  * @private
52
59
  * @ui5-restricted
53
60
  */
54
- function callBoundAction(sActionName: string, contexts: any, oModel: any, oAppComponent: object, mParameters: any) {
61
+ function callBoundAction(sActionName: string, contexts: any, oModel: any, oAppComponent: AppComponent, mParameters: any) {
55
62
  if (!contexts || contexts.length === 0) {
56
63
  //In Freestyle apps bound actions can have no context
57
64
  return Promise.reject("Bound actions always requires at least one context");
@@ -122,7 +129,7 @@ function callBoundAction(sActionName: string, contexts: any, oModel: any, oAppCo
122
129
  * @private
123
130
  * @ui5-restricted
124
131
  */
125
- function callActionImport(sActionName: string, oModel: any, oAppComponent: object, mParameters: any) {
132
+ function callActionImport(sActionName: string, oModel: any, oAppComponent: AppComponent, mParameters: any) {
126
133
  if (!oModel) {
127
134
  return Promise.reject("Action expects a model/context for execution");
128
135
  }
@@ -180,7 +187,7 @@ function _executeFunction(sFunctionName: any, oModel: any, oFunction: any, conte
180
187
  return oFunction.getBoundContext();
181
188
  });
182
189
  }
183
- function callAction(sActionName: any, oModel: any, oAction: any, oAppComponent: any, mParameters: any) {
190
+ function callAction(sActionName: any, oModel: any, oAction: any, oAppComponent: AppComponent, mParameters: any) {
184
191
  return new Promise(async function (resolve: (value: any) => void, reject: (reason?: any) => void) {
185
192
  let mActionExecutionParameters: any = {};
186
193
  let fnDialog;
@@ -210,6 +217,7 @@ function callAction(sActionName: any, oModel: any, oAction: any, oAppComponent:
210
217
  // The parameter ResultIsActiveEntity is always hidden in the dialog! Hence if
211
218
  // this is the only parameter, this is treated as no parameter here because the
212
219
  // dialog would be empty!
220
+ // FIXME: Should only ignore this if this is a 'create' action, otherwise it is just some normal parameter that happens to have this name
213
221
  const bActionNeedsParameterDialog =
214
222
  aActionParameters.length > 0 && !(aActionParameters.length === 1 && aActionParameters[0].$Name === "ResultIsActiveEntity");
215
223
 
@@ -352,10 +360,10 @@ function callAction(sActionName: any, oModel: any, oAction: any, oAppComponent:
352
360
  (element: any) => element.name === mActionExecutionParameters.aActionParameters[i].$Name
353
361
  )?.value;
354
362
  }
355
- } else if (oStartupParameters) {
363
+ } else {
356
364
  for (const i in mActionExecutionParameters.aActionParameters) {
357
365
  mActionExecutionParameters.aActionParameters[i].value =
358
- oStartupParameters[mActionExecutionParameters.aActionParameters[i].$Name][0];
366
+ oStartupParameters[mActionExecutionParameters.aActionParameters[i].$Name]?.[0];
359
367
  }
360
368
  }
361
369
  let oOperationResult: any;
@@ -368,7 +376,7 @@ function callAction(sActionName: any, oModel: any, oAction: any, oAppComponent:
368
376
  mParameters.messageHandler
369
377
  );
370
378
 
371
- const messages = sap.ui.getCore().getMessageManager().getMessageModel().getData();
379
+ const messages = Core.getMessageManager().getMessageModel().getData();
372
380
  if (
373
381
  mActionExecutionParameters.internalModelContext &&
374
382
  mActionExecutionParameters.internalModelContext.getProperty("412Executed") &&
@@ -418,7 +426,7 @@ function callAction(sActionName: any, oModel: any, oAction: any, oAppComponent:
418
426
  }
419
427
  function confirmCriticalAction(
420
428
  sActionName: any,
421
- oAppComponent: any,
429
+ oAppComponent: AppComponent,
422
430
  sActionLabel: any,
423
431
  mParameters: any,
424
432
  aActionParameters: any,
@@ -464,7 +472,7 @@ function confirmCriticalAction(
464
472
  }
465
473
 
466
474
  async function executeAPMAction(
467
- oAppComponent: any,
475
+ oAppComponent: AppComponent,
468
476
  mParameters: any,
469
477
  oParentControl: any,
470
478
  messageHandler: MessageHandler,
@@ -481,7 +489,7 @@ async function executeAPMAction(
481
489
  throw aResult;
482
490
  }
483
491
 
484
- const messages = sap.ui.getCore().getMessageManager().getMessageModel().getData();
492
+ const messages = Core.getMessageManager().getMessageModel().getData();
485
493
  if (
486
494
  mParameters.internalModelContext &&
487
495
  mParameters.internalModelContext.getProperty("412Executed") &&
@@ -638,12 +646,26 @@ function actionParameterShowMessageCallback(
638
646
  *
639
647
  */
640
648
 
649
+ // this type is meant to describe the meta information for one ActionParameter (i.e. its object in metaModel)
650
+ type ActionParameter = {
651
+ $Name: string;
652
+ $isCollection: boolean;
653
+ // currently runtime information is written into the metamodel:
654
+ // - in the press handler of the action button on the parameter dialog, the value of each parameter is added
655
+ // - in setActionParameterDefaultValue, this information is used and transferred to the context (in ODataModel) created for the action execution
656
+ // this is quite odd, and it would make much more sense to take the value from actionParameterInfos
657
+ // - however, setActionParameterDefaultValue (or rather the surrounding _executeAction) is also called from other places
658
+ // => for the time being, adding value here to avoid ts errors, subject to refactoring
659
+ // in case of Field, the value is string, in case of MultiValueField, it's MultiValueFieldItem[]
660
+ value: string | MultiValueFieldItem[];
661
+ };
662
+
641
663
  function showActionParameterDialog(
642
664
  sActionName: any,
643
- oAppComponent: any,
665
+ oAppComponent: AppComponent,
644
666
  sActionLabel: any,
645
667
  mParameters: any,
646
- aActionParameters: any,
668
+ aActionParameters: ActionParameter[],
647
669
  aParameterValues: any,
648
670
  oActionContext: any,
649
671
  oParentControl: any,
@@ -660,84 +682,112 @@ function showActionParameterDialog(
660
682
  bIsCreateAction = mParameters.isCreateAction,
661
683
  sFragmentName = "sap/fe/core/controls/ActionParameterDialog";
662
684
  return new Promise(async function (resolve, reject) {
663
- const oFragment = XMLTemplateProcessor.loadTemplate(sFragmentName, "fragment");
664
- const oParameterModel = new JSONModel({
665
- $displayMode: {}
666
- });
667
- let aFieldInvalid: any[] = [];
668
- let aFormElements: any[] = [];
669
- const mFieldValueMap: any = {};
670
- const validateRequiredProperties = async function () {
671
- const aResults = await Promise.all(
672
- aFormElements
673
- .filter(function (oFormElement: any) {
674
- const oField = oFormElement.getFields()[0];
675
- return oField.getRequired();
676
- })
677
- .map(function (oFormElement: any) {
678
- const value = oFormElement.getFields()[0].isA("sap.ui.mdc.MultiValueField")
679
- ? oFormElement.getFields()[0].getItems()
680
- : oFormElement.getFields()[0].getValue();
681
- if (value === undefined || value === null || value === "" || (Array.isArray(value) && !value.length)) {
682
- return oFormElement;
683
- }
684
- })
685
+ type ActionParameterInfo = {
686
+ parameter: ActionParameter;
687
+ field: Field | MultiValueField;
688
+ isMultiValue: boolean;
689
+ value?: string | MultiValueFieldItem[];
690
+ validationPromise?: Promise<string | MultiValueFieldItem[]>;
691
+ };
692
+ let actionParameterInfos: ActionParameterInfo[]; // to be filled after fragment (for action parameter dialog) is loaded. Actually only needed during dialog processing, i.e. could be moved into the controller and directly initialized there, but only after moving all handlers (esp. press handler for action button) to controller.
693
+
694
+ const messageManager = Core.getMessageManager();
695
+
696
+ // in case of missing mandaotory parameter, message currently differs per parameter, as it superfluously contains the label as parameter. Possiblky this could be removed in future, in that case, interface could be simplified to ActionParameterInfo[], string
697
+ const _addMessageForActionParameter = (messageParameters: { actionParameterInfo: ActionParameterInfo; message: string }[]) => {
698
+ messageManager.addMessages(
699
+ messageParameters.map((messageParameter) => {
700
+ const binding = messageParameter.actionParameterInfo.field.getBinding(
701
+ messageParameter.actionParameterInfo.isMultiValue ? "items" : "value"
702
+ );
703
+ return new Message({
704
+ message: messageParameter.message,
705
+ type: "Error",
706
+ processor: binding?.getModel(),
707
+ persistent: true,
708
+ target: binding?.getResolvedPath()
709
+ });
710
+ })
685
711
  );
686
- return aResults.filter(function (result: any) {
687
- return result !== undefined;
688
- });
689
712
  };
690
- const _validateMessages = function (actionParameters: any, invalidFields: any, bClearTarget?: boolean) {
691
- const oMessageManager = Core.getMessageManager();
692
- const aMessages = oMessageManager.getMessageModel().getData();
693
713
 
694
- invalidFields = invalidFields || [];
714
+ const _removeMessagesForActionParamter = (parameter: ActionParameter) => {
715
+ const allMessages = messageManager.getMessageModel().getData();
716
+ const controlId = generate(["APD_", parameter.$Name]);
717
+ // also remove messages assigned to inner controls, but avoid removing messages for different paramters (with name being substring of another parameter name)
718
+ const relevantMessages = allMessages.filter((msg: Message) =>
719
+ msg.getControlIds().some((id: string) => controlId.split("-").includes(id))
720
+ );
721
+ messageManager.removeMessages(relevantMessages);
722
+ };
695
723
 
696
- if (!aMessages.length) {
697
- invalidFields = [];
698
- }
699
- actionParameters.forEach(function (oActionParameters: any) {
700
- const sParameter = oActionParameters.$Name;
701
- aMessages.forEach(function (oMessage: any) {
702
- const sParam = sParameter.replace("-inner", "");
703
- if (
704
- oMessage.controlIds.length > 0 &&
705
- (oMessage.getControlId().includes(`APD_::${sParameter}`) ||
706
- (oMessage.getControlId().includes(`APD_::${sParameter}inner`) && invalidFields.indexOf(`APD_::${sParam}`) < 0))
707
- ) {
708
- if (bClearTarget) {
709
- oMessageManager.removeMessages(oMessage);
710
- } else {
711
- invalidFields.push(`APD_::${sParam}`);
712
- }
713
- }
714
- // Handle messages related to input with invalid token
715
- if (oMessage.target.includes(`APD_::${sParameter}`)) {
716
- invalidFields.push(`APD_::${sParam}`);
717
- oMessage.target = bClearTarget ? "" : oMessage.target;
718
- if (bClearTarget) {
719
- oMessageManager.removeMessages(oMessage);
720
- }
721
- }
722
- });
723
- });
724
- return invalidFields;
724
+ const _validateRequiredProperties = async function (oResourceBundle: ResourceBundle) {
725
+ const requiredParameterInfos = actionParameterInfos.filter((actionParameterInfo) => actionParameterInfo.field.getRequired());
726
+ // before validation of required fields is not complete, we cannot know the values and thus cannot validate they are provided
727
+ const validationResults = await Promise.allSettled(
728
+ requiredParameterInfos.map((actionParameterInfo) => actionParameterInfo.validationPromise)
729
+ );
730
+
731
+ const validRequiredFields = validationResults
732
+ .map((validationResult, index) => ({
733
+ validationResult: validationResult,
734
+ index: index
735
+ }))
736
+ .filter((validationResult) => validationResult.validationResult.status === "fulfilled")
737
+ .map((validationResult) => requiredParameterInfos[validationResult.index]);
738
+
739
+ const emptyRequiredFields = validRequiredFields.filter((actionParameterInfo) =>
740
+ actionParameterInfo.isMultiValue ? !(actionParameterInfo.value as MultiValueFieldItem[]).length : !actionParameterInfo.value
741
+ );
742
+ if (!emptyRequiredFields.length) return true;
743
+
744
+ // message contains label per field for historical reason (originally, it was shown in additional popup, now it's directly added to the field)
745
+ // if this was not the case (and hopefully, in future this might be subject to change), interface of _addMessageForActionParameter could be simplified to just pass emptyRequiredFields and a constant message here
746
+ _addMessageForActionParameter(
747
+ emptyRequiredFields.map((actionParameterInfo) => ({
748
+ actionParameterInfo: actionParameterInfo,
749
+ message: CommonUtils.getTranslatedText("C_OPERATIONS_ACTION_PARAMETER_DIALOG_MISSING_MANDATORY_MSG", oResourceBundle, [
750
+ (actionParameterInfo.field.getParent()?.getAggregation("label") as Label).getText()
751
+ ])
752
+ }))
753
+ );
754
+
755
+ // Set the focus on the dialog's first erroneous required control
756
+ emptyRequiredFields[0].field.focus();
757
+
758
+ return false;
725
759
  };
760
+
726
761
  const oController = {
727
- handleChange: function (oEvent: any) {
728
- messageHandler.removeTransitionMessages();
729
- const oField = oEvent.getSource();
730
- const sFieldId = oEvent.getParameter("id");
731
- const oFieldPromise = oEvent.getParameter("promise");
732
- if (oFieldPromise) {
733
- mFieldValueMap[sFieldId] = oFieldPromise.then(function () {
734
- return oField.getValue();
735
- });
762
+ handleChange: async function (oEvent: Event) {
763
+ const field = oEvent.getSource();
764
+ const actionParameterInfo = actionParameterInfos.find(
765
+ (actionParameterInfo) => actionParameterInfo.field === field
766
+ ) as ActionParameterInfo;
767
+ // field value is being changed, thus existing messages related to that field are not valid anymore
768
+ _removeMessagesForActionParamter(actionParameterInfo.parameter);
769
+ // adapt info. Promise is resolved to value or rejected with exception containing message
770
+ actionParameterInfo.validationPromise = oEvent.getParameter("promise") as Promise<string>;
771
+
772
+ try {
773
+ actionParameterInfo.value = await actionParameterInfo.validationPromise;
774
+ } catch (error) {
775
+ delete actionParameterInfo.value;
776
+ _addMessageForActionParameter([
777
+ {
778
+ actionParameterInfo: actionParameterInfo,
779
+ message: (error as { message: string }).message
780
+ }
781
+ ]);
736
782
  }
737
- _validateMessages(aActionParameters, aFieldInvalid);
738
783
  }
739
784
  };
740
785
 
786
+ const oFragment = XMLTemplateProcessor.loadTemplate(sFragmentName, "fragment");
787
+ const oParameterModel = new JSONModel({
788
+ $displayMode: {}
789
+ });
790
+
741
791
  try {
742
792
  const createdFragment = await XMLPreprocessor.process(
743
793
  oFragment,
@@ -762,20 +812,38 @@ function showActionParameterDialog(
762
812
  // eslint-disable-next-line prefer-const
763
813
  let oOperationBinding: any;
764
814
  await CommonUtils.setUserDefaults(oAppComponent, aActionParameters, oParameterModel, true);
765
- const oDialogContent = (await Fragment.load({ definition: createdFragment, controller: oController })) as Control;
815
+ const oDialogContent = (await Fragment.load({
816
+ definition: createdFragment,
817
+ controller: oController
818
+ })) as Control;
819
+
820
+ actionParameterInfos = aActionParameters.map((actionParameter) => {
821
+ const field = Core.byId(generate(["APD_", actionParameter.$Name])) as Field | MultiValueField;
822
+ const isMultiValue = field.isA("sap.ui.mdc.MultiValueField");
823
+ return {
824
+ parameter: actionParameter,
825
+ field: field,
826
+ isMultiValue: isMultiValue
827
+ };
828
+ });
829
+
766
830
  const oResourceBundle = oParentControl.getController().oResourceBundle;
831
+ let actionResult = {
832
+ dialogCancelled: true, // to be set to false in case of successful action exection
833
+ result: undefined
834
+ };
767
835
  const oDialog = new Dialog(undefined, {
768
836
  title: sActionLabel || CommonUtils.getTranslatedText("C_OPERATIONS_ACTION_PARAMETER_DIALOG_TITLE", oResourceBundle),
769
837
  content: [oDialogContent],
770
838
  escapeHandler: function () {
771
839
  // escape handler is meant to possibly suppress or postpone closing the dialog on escape (by calling "reject" on the provided object, or "resolve" only when
772
840
  // done with all tasks to happen before dialog can be closed). It's not intended to explicetly close the dialog here (that happens automatically when no
773
- // escapeHandler is provided or the resolve-calllback is calle) or for own wrap up tasks (like removing validition messages - this should happen in the
841
+ // escapeHandler is provided or the resolve-callback is called) or for own wrap up tasks (like removing validition messages - this should happen in the
774
842
  // afterClose).
775
843
  // TODO: Move wrap up tasks to afterClose, and remove this method completely. Take care to also adapt end button press handler accordingly.
844
+ // Currently only still needed to differentiate closing dialog after successful execution (uses resolve) from user cancellation (using reject)
776
845
  oDialog.close();
777
- messageHandler.removeTransitionMessages();
778
- reject(Constants.CancelActionDialog);
846
+ // reject(Constants.CancelActionDialog);
779
847
  },
780
848
  beginButton: new Button(generate(["fe", "APD_", sActionName, "Action", "Ok"]), {
781
849
  text: bIsCreateAction
@@ -784,37 +852,13 @@ function showActionParameterDialog(
784
852
  type: "Emphasized",
785
853
  press: async function () {
786
854
  try {
787
- const aEmptyMandatoryFields = await validateRequiredProperties();
788
- if (aEmptyMandatoryFields.length) {
789
- for (let i = 0; i < aEmptyMandatoryFields.length; i++) {
790
- aEmptyMandatoryFields[i].getFields()[0].setValueState("Error");
791
- aEmptyMandatoryFields[i]
792
- .getFields()[0]
793
- .setValueStateText(
794
- CommonUtils.getTranslatedText(
795
- "C_OPERATIONS_ACTION_PARAMETER_DIALOG_MISSING_MANDATORY_MSG",
796
- oResourceBundle,
797
- aEmptyMandatoryFields[i].getLabel().getText()
798
- )
799
- );
800
- }
855
+ if (!(await _validateRequiredProperties(oResourceBundle))) {
801
856
  return;
802
857
  }
803
858
 
804
- aFieldInvalid = _validateMessages(aActionParameters, aFieldInvalid);
805
-
806
- if (aFieldInvalid.length > 0) {
807
- await messageHandling.showUnboundMessages();
808
- return;
809
- }
810
859
  BusyLocker.lock(oDialog);
811
860
 
812
861
  try {
813
- await Promise.all(
814
- Object.keys(mFieldValueMap).map(function (sKey: string) {
815
- return mFieldValueMap[sKey];
816
- })
817
- );
818
862
  // TODO: due to using the search and value helps on the action dialog transient messages could appear
819
863
  // we need an UX design for those to show them to the user - for now remove them before continuing
820
864
  messageHandler.removeTransitionMessages();
@@ -832,7 +876,7 @@ function showActionParameterDialog(
832
876
  } else {
833
877
  vParameterValue = oParameterContext.getProperty(aActionParameters[i].$Name);
834
878
  }
835
- aActionParameters[i].value = vParameterValue;
879
+ aActionParameters[i].value = vParameterValue; // writing the current value (ueser input!) into the metamodel => should be refactored to use ActionParameterInfos instead. Used in setActionParameterDefaultValue
836
880
  vParameterValue = undefined;
837
881
  }
838
882
  mParameters.label = sActionLabel;
@@ -847,8 +891,11 @@ function showActionParameterDialog(
847
891
  oDialog,
848
892
  false
849
893
  );
894
+ actionResult = {
895
+ dialogCancelled: false,
896
+ result: aResult
897
+ };
850
898
  oDialog.close();
851
- resolve(aResult);
852
899
  } catch (oError: any) {
853
900
  const messages = sap.ui.getCore().getMessageManager().getMessageModel().getData();
854
901
  if (
@@ -887,7 +934,10 @@ function showActionParameterDialog(
887
934
 
888
935
  mParameters.internalModelContext.setProperty("strictHandlingFails", []);
889
936
  mParameters.internalModelContext.setProperty("processedMessageIds", []);
890
- resolve(aResult);
937
+ actionResult = {
938
+ dialogCancelled: false,
939
+ result: aResult
940
+ };
891
941
  } catch {
892
942
  if (
893
943
  mParameters.internalModelContext &&
@@ -959,13 +1009,7 @@ function showActionParameterDialog(
959
1009
  text: CommonUtils.getTranslatedText("C_COMMON_ACTION_PARAMETER_DIALOG_CANCEL", oResourceBundle),
960
1010
  press: function () {
961
1011
  // TODO: cancel button should just close the dialog (similar to using escape). All wrap up tasks should be moved to afterClose.
962
- // Assumption: _validateMessages is only called to remove exisitng validation messages (is user first enters invalid parameter, and later cancels).
963
- // If this assumption is correct, this needs also to be done when leaving the dialog with escape, i.e. should be moved to afterClose.
964
- _validateMessages(aActionParameters, aFieldInvalid, true);
965
1012
  oDialog.close();
966
- // should not be done here, but after close, as the same should happen when leaving with escape
967
- messageHandler.removeTransitionMessages();
968
- reject(Constants.CancelActionDialog);
969
1013
  }
970
1014
  }),
971
1015
  // TODO: beforeOpen is just an event, i.e. not waiting for the Promise to be resolved. Check if tasks of this function need to be done before opening the dialog
@@ -986,63 +1030,60 @@ function showActionParameterDialog(
986
1030
  };
987
1031
  const fnSetDefaultsAndOpenDialog = async function (sBindingParameter?: any) {
988
1032
  const sBoundFunctionName = getDefaultValuesFunction();
989
- const prefillParameter = function (sParamName: any, vParamDefaultValue: any) {
990
- // eslint-disable-next-line promise/param-names
991
- return new Promise(async function (inResolve) {
992
- // Case 1: There is a ParameterDefaultValue annotation
993
- if (vParamDefaultValue !== undefined) {
994
- if (aContexts.length > 0 && vParamDefaultValue.$Path) {
995
- try {
996
- let vParamValue = await CommonUtils.requestSingletonProperty(
997
- vParamDefaultValue.$Path,
998
- oOperationBinding.getModel()
999
- );
1000
- if (vParamValue === null) {
1001
- vParamValue = await oOperationBinding
1002
- .getParameterContext()
1003
- .requestProperty(vParamDefaultValue.$Path);
1033
+ const prefillParameter = async function (sParamName: any, vParamDefaultValue: any) {
1034
+ // Case 1: There is a ParameterDefaultValue annotation
1035
+ if (vParamDefaultValue !== undefined) {
1036
+ if (aContexts.length > 0 && vParamDefaultValue.$Path) {
1037
+ try {
1038
+ let vParamValue = await CommonUtils.requestSingletonProperty(
1039
+ vParamDefaultValue.$Path,
1040
+ oOperationBinding.getModel()
1041
+ );
1042
+ if (vParamValue === null) {
1043
+ vParamValue = await oOperationBinding
1044
+ .getParameterContext()
1045
+ .requestProperty(vParamDefaultValue.$Path);
1046
+ }
1047
+ if (aContexts.length > 1) {
1048
+ // For multi select, need to loop over aContexts (as contexts cannot be retrieved via binding parameter of the operation binding)
1049
+ let sPathForContext = vParamDefaultValue.$Path;
1050
+ if (sPathForContext.indexOf(`${sBindingParameter}/`) === 0) {
1051
+ sPathForContext = sPathForContext.replace(`${sBindingParameter}/`, "");
1004
1052
  }
1005
- if (aContexts.length > 1) {
1006
- // For multi select, need to loop over aContexts (as contexts cannot be retrieved via binding parameter of the operation binding)
1007
- let sPathForContext = vParamDefaultValue.$Path;
1008
- if (sPathForContext.indexOf(`${sBindingParameter}/`) === 0) {
1009
- sPathForContext = sPathForContext.replace(`${sBindingParameter}/`, "");
1010
- }
1011
- for (let i = 1; i < aContexts.length; i++) {
1012
- if (aContexts[i].getProperty(sPathForContext) !== vParamValue) {
1013
- // if the values from the contexts are not all the same, do not prefill
1014
- inResolve({
1015
- paramName: sParamName,
1016
- value: undefined,
1017
- bNoPossibleValue: true
1018
- });
1019
- }
1053
+ for (let i = 1; i < aContexts.length; i++) {
1054
+ if (aContexts[i].getProperty(sPathForContext) !== vParamValue) {
1055
+ // if the values from the contexts are not all the same, do not prefill
1056
+ return {
1057
+ paramName: sParamName,
1058
+ value: undefined,
1059
+ bNoPossibleValue: true
1060
+ };
1020
1061
  }
1021
1062
  }
1022
- inResolve({ paramName: sParamName, value: vParamValue });
1023
- } catch (oError) {
1024
- Log.error("Error while reading default action parameter", sParamName, mParameters.actionName);
1025
- inResolve({
1026
- paramName: sParamName,
1027
- value: undefined,
1028
- bLatePropertyError: true
1029
- });
1030
1063
  }
1031
- } else {
1032
- // Case 1.2: ParameterDefaultValue defines a fixed string value (i.e. vParamDefaultValue = 'someString')
1033
- inResolve({ paramName: sParamName, value: vParamDefaultValue });
1064
+ return { paramName: sParamName, value: vParamValue };
1065
+ } catch (oError) {
1066
+ Log.error("Error while reading default action parameter", sParamName, mParameters.actionName);
1067
+ return {
1068
+ paramName: sParamName,
1069
+ value: undefined,
1070
+ bLatePropertyError: true
1071
+ };
1034
1072
  }
1035
- } else if (oParameterModel && (oParameterModel as any).oData[sParamName]) {
1036
- // Case 2: There is no ParameterDefaultValue annotation (=> look into the FLP User Defaults)
1037
-
1038
- inResolve({
1039
- paramName: sParamName,
1040
- value: (oParameterModel as any).oData[sParamName]
1041
- });
1042
1073
  } else {
1043
- inResolve({ paramName: sParamName, value: undefined });
1074
+ // Case 1.2: ParameterDefaultValue defines a fixed string value (i.e. vParamDefaultValue = 'someString')
1075
+ return { paramName: sParamName, value: vParamDefaultValue };
1044
1076
  }
1045
- });
1077
+ } else if (oParameterModel && (oParameterModel as any).oData[sParamName]) {
1078
+ // Case 2: There is no ParameterDefaultValue annotation (=> look into the FLP User Defaults)
1079
+
1080
+ return {
1081
+ paramName: sParamName,
1082
+ value: (oParameterModel as any).oData[sParamName]
1083
+ };
1084
+ } else {
1085
+ return { paramName: sParamName, value: undefined };
1086
+ }
1046
1087
  };
1047
1088
 
1048
1089
  const getParameterDefaultValue = function (sParamName: any) {
@@ -1184,16 +1225,28 @@ function showActionParameterDialog(
1184
1225
  };
1185
1226
 
1186
1227
  await fnAsyncBeforeOpen();
1228
+
1229
+ // adding defaulted values only here after they are not set to the fields
1230
+ for (const actionParameterInfo of actionParameterInfos) {
1231
+ const value = actionParameterInfo.isMultiValue
1232
+ ? (actionParameterInfo.field as MultiValueField).getItems()
1233
+ : (actionParameterInfo.field as Field).getValue();
1234
+ actionParameterInfo.value = value;
1235
+ actionParameterInfo.validationPromise = Promise.resolve(value);
1236
+ }
1187
1237
  },
1188
1238
  afterClose: function () {
1239
+ // when the dialog is cancelled, messages need to be removed in case the same action should be executed again
1240
+ aActionParameters.forEach(_removeMessagesForActionParamter);
1189
1241
  oDialog.destroy();
1242
+ if (actionResult.dialogCancelled) {
1243
+ reject(Constants.CancelActionDialog);
1244
+ } else {
1245
+ resolve(actionResult.result);
1246
+ }
1190
1247
  }
1191
1248
  });
1192
1249
  mParameters.oDialog = oDialog;
1193
- aFormElements = (oDialogContent as any)
1194
- .getAggregation("form")
1195
- .getAggregation("formContainers")[0]
1196
- .getAggregation("formElements");
1197
1250
  oDialog.setModel(oActionContext.getModel().oModel);
1198
1251
  oDialog.setModel(oParameterModel, "paramsModel");
1199
1252
  oDialog.bindElement({
@@ -1411,6 +1464,7 @@ function executeDependingOnSelectedContexts(
1411
1464
  messageHandler,
1412
1465
  oResourceBundle
1413
1466
  );
1467
+
1414
1468
  return Promise.reject();
1415
1469
  })
1416
1470
  : oAction
@@ -1531,45 +1585,36 @@ function _executeAction(oAppComponent: any, mParameters: any, oParentControl?: a
1531
1585
  fnRequestSideEffects(oAppComponent, oSideEffect, mParameters, sGroupId);
1532
1586
  };
1533
1587
  const fnExecuteSingleAction = function (actionContext: any, current_context_index: any, oSideEffect: any, iContextLength: any) {
1534
- // eslint-disable-next-line promise/param-names
1535
- return new Promise<void>((actionResolve) => {
1536
- const aLocalPromise: any = [];
1537
- setActionParameterDefaultValue();
1538
- // For invocation grouping "isolated" need batch group per action call
1539
- sGroupId = `apiMode${current_context_index}`;
1540
- mParameters.requestSideEffects = fnRequestSideEffects.bind(
1541
- operations,
1542
- oAppComponent,
1543
- oSideEffect,
1544
- mParameters,
1545
- sGroupId,
1546
- aLocalPromise
1547
- );
1548
- oActionPromise = executeDependingOnSelectedContexts(
1549
- actionContext,
1550
- mParameters,
1551
- bGetBoundContext,
1552
- sGroupId,
1553
- oResourceBundle,
1554
- messageHandler,
1555
- iContextLength,
1556
- current_context_index
1557
- );
1558
- aActionPromises.push(oActionPromise);
1559
- aLocalPromise.push(oActionPromise);
1560
- fnRequestSideEffects(oAppComponent, oSideEffect, mParameters, sGroupId, aLocalPromise);
1561
- oModel.submitBatch(sGroupId);
1562
- Promise.all(aLocalPromise)
1563
- .then(function () {
1564
- return actionResolve();
1565
- })
1566
- .catch(function () {
1567
- return actionResolve();
1568
- });
1569
- });
1588
+ const aLocalPromise: any = [];
1589
+ setActionParameterDefaultValue();
1590
+ // For invocation grouping "isolated" need batch group per action call
1591
+ sGroupId = `apiMode${current_context_index}`;
1592
+ mParameters.requestSideEffects = fnRequestSideEffects.bind(
1593
+ operations,
1594
+ oAppComponent,
1595
+ oSideEffect,
1596
+ mParameters,
1597
+ sGroupId,
1598
+ aLocalPromise
1599
+ );
1600
+ oActionPromise = executeDependingOnSelectedContexts(
1601
+ actionContext,
1602
+ mParameters,
1603
+ bGetBoundContext,
1604
+ sGroupId,
1605
+ oResourceBundle,
1606
+ messageHandler,
1607
+ iContextLength,
1608
+ current_context_index
1609
+ );
1610
+ aActionPromises.push(oActionPromise);
1611
+ aLocalPromise.push(oActionPromise);
1612
+ fnRequestSideEffects(oAppComponent, oSideEffect, mParameters, sGroupId, aLocalPromise);
1613
+ oModel.submitBatch(sGroupId);
1614
+ return Promise.allSettled(aLocalPromise);
1570
1615
  };
1571
1616
 
1572
- function fnExecuteSequentially(contextsToExecute: any) {
1617
+ async function fnExecuteSequentially(contextsToExecute: Context[]) {
1573
1618
  // One action and its side effects are completed before the next action is executed
1574
1619
  (
1575
1620
  fnOnSubmitted ||
@@ -1591,22 +1636,13 @@ function _executeAction(oAppComponent: any, mParameters: any, oParentControl?: a
1591
1636
  );
1592
1637
  }
1593
1638
 
1594
- let oActionAndSideEffectPromise = Promise.resolve();
1595
- let j = 0;
1596
- contextsToExecute.forEach(function (context: any) {
1597
- const id = ++j;
1598
- oActionAndSideEffectPromise = oActionAndSideEffectPromise.then(function () {
1599
- return processOneAction(context, id, aContexts.length);
1600
- });
1601
- });
1639
+ // serialization: processOneAction to be called for each entry in contextsToExecute only after the promise returned from the one before has been resolved
1640
+ await contextsToExecute.reduce(async (promise: Promise<void>, context: Context, id: int): Promise<void> => {
1641
+ await promise;
1642
+ await processOneAction(context, id + 1, aContexts.length);
1643
+ }, Promise.resolve());
1602
1644
 
1603
- oActionAndSideEffectPromise
1604
- .then(function () {
1605
- fnHandleResults();
1606
- })
1607
- .catch(function (oError: any) {
1608
- Log.error(oError);
1609
- });
1645
+ fnHandleResults();
1610
1646
  }
1611
1647
 
1612
1648
  if (!bGrouped) {