Skip to content

Commit b002ea3

Browse files
new afctl sample && polish here and there
1 parent c72d1c2 commit b002ea3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+3980
-44
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"destinations": [
3+
{
4+
"name": "northwind",
5+
"url": "https://services.odata.org"
6+
},
7+
{
8+
"name": "ui5",
9+
"url": "https://ui5.sap.com"
10+
}
11+
]
12+
}
92.6 KB
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "application-frontend-afctl",
3+
"version": "1.0.0",
4+
"scripts": {
5+
"start": "ui5 serve",
6+
"build": "ui5 build",
7+
"deploy": "afctl push dist -c conf.json -l"
8+
},
9+
"dependencies": {
10+
"@ui5/cli": "latest"
11+
}
12+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# SAP Fiori Application using the Application Frontend Service CLI (afctl)
2+
3+
## Diagram
4+
5+
![diagram](diagram.png)
6+
7+
## Description
8+
9+
This an example of an SAP Fiori application that is deployed to the [Application Frontend service](https://help.sap.com/docs/application-frontend-service/application-frontend-service/what-is-application-frontend-service) via its CLI ([afctl](https://www.npmjs.com/package/@sap/appfront-cli)). The Application Frontend service comes with the capabilities of the [application router](https://www.npmjs.com/package/@sap/approuter), so applications must include an [xs-app.json](./webapp/xs-app.json) file (also see [minimal requirements](https://help.sap.com/docs/application-frontend-service/application-frontend-service/preparing-applications-for-application-frontend-service)). Additional configuration such as destinations (via environment variables) are provided during deployment via the [conf.json](./conf.json) file.
10+
11+
## Download and Deployment
12+
13+
1. Follow the [initial setup guide](https://help.sap.com/docs/application-frontend-service/application-frontend-service/initial-setup) for the Application Frontend service.
14+
1. Install the Application Frontend service CLI (afctl) and login according to its [documentation](https://www.npmjs.com/package/@sap/appfront-cli).
15+
1. Download the source code:
16+
```
17+
git clone https://github.com/SAP-samples/multi-cloud-html5-apps-samples
18+
cd multi-cloud-html5-apps-samples/application-frontend-afctl
19+
```
20+
1. Build the project:
21+
```
22+
npm install
23+
npm run build
24+
```
25+
1. Deploy the project:
26+
```
27+
afctl push dist -c conf.json -l
28+
```
29+
1. List the deployed HTML5 apps:
30+
```
31+
afctl list
32+
```
33+
34+
## Check the Result
35+
36+
### List the Deployed HTML5 Apps
37+
```
38+
$ afctl list
39+
OK
40+
41+
Applications
42+
43+
Name Version URL
44+
sample.manage.products.app.front.afctl 0.0.1 https://samplemanageproductsappfrontafctl-ge8od.eu12.appfront.cloud.sap
45+
```
46+
47+
### Check the SAP Fiori Application
48+
49+
Access one of the URLs described in the [Download and Deployment](#download-and-deployment) section. You are redirected to a sign-on page before you can see the app.
50+
51+
![SAP Fiori Application](result.png)
261 KB
Loading
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
specVersion: "3.2"
2+
metadata:
3+
name: products-cli
4+
type: application
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
sap.ui.define([
2+
"sap/ui/core/UIComponent",
3+
"sap/ui/Device",
4+
"./model/models",
5+
"./controller/ErrorHandler"
6+
], function (UIComponent, Device, models, ErrorHandler) {
7+
"use strict";
8+
9+
return UIComponent.extend("ns.manageproducts.Component", {
10+
11+
metadata : {
12+
manifest: "json"
13+
},
14+
15+
/**
16+
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
17+
* In this function, the device models are set and the router is initialized.
18+
* @public
19+
* @override
20+
*/
21+
init : function () {
22+
// call the base component's init function
23+
UIComponent.prototype.init.apply(this, arguments);
24+
25+
// initialize the error handler with the component
26+
this._oErrorHandler = new ErrorHandler(this);
27+
28+
// set the device model
29+
this.setModel(models.createDeviceModel(), "device");
30+
31+
// set the product feedback model
32+
this.setModel(models.createCommentsModel(), "productFeedback");
33+
34+
// create the views based on the url/hash
35+
this.getRouter().initialize();
36+
},
37+
38+
/**
39+
* The component is destroyed by UI5 automatically.
40+
* In this method, the ErrorHandler is destroyed.
41+
* @public
42+
* @override
43+
*/
44+
destroy : function () {
45+
this._oErrorHandler.destroy();
46+
// call the base component's destroy function
47+
UIComponent.prototype.destroy.apply(this, arguments);
48+
},
49+
50+
/**
51+
* This method can be called to determine whether the sapUiSizeCompact or sapUiSizeCozy
52+
* design mode class should be set, which influences the size appearance of some controls.
53+
* @public
54+
* @return {string} css class, either 'sapUiSizeCompact' or 'sapUiSizeCozy' - or an empty string if no css class should be set
55+
*/
56+
getContentDensityClass : function() {
57+
if (this._sContentDensityClass === undefined) {
58+
// check whether FLP has already set the content density class; do nothing in this case
59+
if (document.body.classList.contains("sapUiSizeCozy") || document.body.classList.contains("sapUiSizeCompact")) {
60+
this._sContentDensityClass = "";
61+
} else if (!Device.support.touch) { // apply "compact" mode if touch is not supported
62+
this._sContentDensityClass = "sapUiSizeCompact";
63+
} else {
64+
// "cozy" in case of touch support; default for most sap.m controls, but needed for desktop-first controls like sap.ui.table.Table
65+
this._sContentDensityClass = "sapUiSizeCozy";
66+
}
67+
}
68+
return this._sContentDensityClass;
69+
}
70+
71+
});
72+
73+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
sap.ui.define([
2+
"./BaseController",
3+
"sap/ui/model/json/JSONModel"
4+
], function (BaseController, JSONModel) {
5+
"use strict";
6+
7+
return BaseController.extend("ns.manageproducts.controller.App", {
8+
9+
onInit : function () {
10+
var oViewModel,
11+
fnSetAppNotBusy,
12+
iOriginalBusyDelay = this.getView().getBusyIndicatorDelay();
13+
14+
oViewModel = new JSONModel({
15+
busy : true,
16+
delay : 0
17+
});
18+
this.setModel(oViewModel, "appView");
19+
20+
fnSetAppNotBusy = function() {
21+
oViewModel.setProperty("/busy", false);
22+
oViewModel.setProperty("/delay", iOriginalBusyDelay);
23+
};
24+
25+
// disable busy indication when the metadata is loaded and in case of errors
26+
this.getOwnerComponent().getModel().metadataLoaded().
27+
then(fnSetAppNotBusy);
28+
this.getOwnerComponent().getModel().attachMetadataFailed(fnSetAppNotBusy);
29+
30+
// apply content density mode to root view
31+
this.getView().addStyleClass(this.getOwnerComponent().getContentDensityClass());
32+
}
33+
34+
});
35+
36+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
sap.ui.define([
2+
"sap/ui/core/mvc/Controller",
3+
"sap/ui/core/UIComponent",
4+
"sap/m/library"
5+
], function (Controller, UIComponent, mobileLibrary) {
6+
"use strict";
7+
8+
return Controller.extend("ns.manageproducts.controller.BaseController", {
9+
/**
10+
* Convenience method for accessing the router.
11+
* @public
12+
* @returns {sap.ui.core.routing.Router} the router for this component
13+
*/
14+
getRouter : function () {
15+
return UIComponent.getRouterFor(this);
16+
},
17+
18+
/**
19+
* Convenience method for getting the view model by name.
20+
* @public
21+
* @param {string} [sName] the model name
22+
* @returns {sap.ui.model.Model} the model instance
23+
*/
24+
getModel : function (sName) {
25+
return this.getView().getModel(sName);
26+
},
27+
28+
/**
29+
* Convenience method for setting the view model.
30+
* @public
31+
* @param {sap.ui.model.Model} oModel the model instance
32+
* @param {string} sName the model name
33+
* @returns {sap.ui.mvc.View} the view instance
34+
*/
35+
setModel : function (oModel, sName) {
36+
return this.getView().setModel(oModel, sName);
37+
},
38+
39+
/**
40+
* Getter for the resource bundle.
41+
* @public
42+
* @returns {sap.ui.model.resource.ResourceModel} the resourceModel of the component
43+
*/
44+
getResourceBundle : function () {
45+
return this.getOwnerComponent().getModel("i18n").getResourceBundle();
46+
},
47+
48+
/**
49+
* Event handler when the share by E-Mail button has been clicked
50+
* @public
51+
*/
52+
onShareEmailPress : function () {
53+
var oViewModel = (this.getModel("objectView") || this.getModel("worklistView"));
54+
mobileLibrary.URLHelper.triggerEmail(
55+
null,
56+
oViewModel.getProperty("/shareSendEmailSubject"),
57+
oViewModel.getProperty("/shareSendEmailMessage")
58+
);
59+
}
60+
61+
});
62+
63+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
sap.ui.define([
2+
"sap/ui/base/Object",
3+
"sap/m/MessageBox"
4+
], function (UI5Object, MessageBox) {
5+
"use strict";
6+
7+
return UI5Object.extend("ns.manageproducts.controller.ErrorHandler", {
8+
9+
/**
10+
* Handles application errors by automatically attaching to the model events and displaying errors when needed.
11+
* @class
12+
* @param {sap.ui.core.UIComponent} oComponent reference to the app's component
13+
* @public
14+
* @alias mycompany.myapp.MyWorklistApp.controller.ErrorHandler
15+
*/
16+
constructor : function (oComponent) {
17+
this._oResourceBundle = oComponent.getModel("i18n").getResourceBundle();
18+
this._oComponent = oComponent;
19+
this._oModel = oComponent.getModel();
20+
this._bMessageOpen = false;
21+
this._sErrorText = this._oResourceBundle.getText("errorText");
22+
23+
this._oModel.attachMetadataFailed(function (oEvent) {
24+
var oParams = oEvent.getParameters();
25+
this._showServiceError(oParams.response);
26+
}, this);
27+
28+
this._oModel.attachRequestFailed(function (oEvent) {
29+
var oParams = oEvent.getParameters();
30+
// An entity that was not found in the service is also throwing a 404 error in oData.
31+
// We already cover this case with a notFound target so we skip it here.
32+
// A request that cannot be sent to the server is a technical error that we have to handle though
33+
if (oParams.response.statusCode !== "404" || (oParams.response.statusCode === 404 && oParams.response.responseText.indexOf("Cannot POST") === 0)) {
34+
this._showServiceError(oParams.response);
35+
}
36+
}, this);
37+
},
38+
39+
/**
40+
* Shows a {@link sap.m.MessageBox} when a service call has failed.
41+
* Only the first error message will be display.
42+
* @param {string} sDetails a technical error to be displayed on request
43+
* @private
44+
*/
45+
_showServiceError : function (sDetails) {
46+
if (this._bMessageOpen) {
47+
return;
48+
}
49+
this._bMessageOpen = true;
50+
MessageBox.error(
51+
this._sErrorText,
52+
{
53+
id : "serviceErrorMessageBox",
54+
details: sDetails,
55+
styleClass: this._oComponent.getContentDensityClass(),
56+
actions: [MessageBox.Action.CLOSE],
57+
onClose: function () {
58+
this._bMessageOpen = false;
59+
}.bind(this)
60+
}
61+
);
62+
}
63+
64+
});
65+
66+
});

0 commit comments

Comments
 (0)