Skip to Content
Technical Articles
Author's profile photo Ferry Djaja

SAP Analytics Cloud Barcode Scanner Custom Widget

In this blog, I will demonstrate how to create a barcode scanner custom widget in SAP Analytics Cloud.

What component we need to have:

  • SAPUI5 components: sap.m.Label, sap.m.Input, sap.m.Button, sap.ui.layout.form.SimpleForm.
  • For barcode scanner, we will be using library from quaggaJS.

Custom Widget JSON

Let start with creating a  custom widget JSON file, barcodescanner.json. In this file, I added the valid properties for Root object, Webcomponent object and Properties object. I am not using the Methods and Events object.

{
  "id": "com.fd.djaja.sap.sac.scanner",
  "version": "0.0.2",
  "name": "barcodeScan",
  "description": "Barcode Scanner",
  "newInstancePrefix": "barcodeScan",
  "icon": "data:image/png;base64,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",
  "vendor": "Ferry Djaja",
  "eula": "",
  "license": "",
  "imports": [ "input-controls" ],
  "webcomponents": [
    {
      "kind": "main",
      "tag": "com-fd-djaja-sap-sac-scanner",
      "url": "http://localhost/SAC/sacbarcodescanner/barcodescanner.js",
      "integrity": "",
      "ignoreIntegrity": true
    }
  ],
  "properties": {
    "metadata": {
      "type": "string",
      "description": "For internal use",
      "default": ""
    }
  },
  "methods": {
  },
  "events": {
  }
}

Web Component JavaScript

We will implement the following Web Component JavaScript functions:

  • constructor() It is fired when the Web Component is initialized. 
    In this function, we create the shadow DOM root element, copy of the template element is added as a child element to the shadow DOM root element, create the unique ID (guid) for the HTML div and set the variable _firstConnection.

    constructor() {
        super();
    
        _shadowRoot = this.attachShadow({
            mode: "open"
        });
        _shadowRoot.appendChild(tmpl.content.cloneNode(true));
    
        this._id = createGuid();
    
        _shadowRoot.querySelector("#export_div").id = this._id + "_export_div";
    
        this._firstConnection = 0;
    }​

     

  • connectedCallback() It is fired when the widget is added to the html DOM of the page. 
    In this function we check whether the app is in the edit mode or not and to list the available components (e.g,. Button, Text, Input Field, Panel, etc) in variable (this.metadata)["components"] for future use.

    connectedCallback() {
        try {
            if (window.commonApp) {
                let outlineContainer = commonApp.getShell().findElements(true, ele => ele.hasStyleClass && ele.hasStyleClass("sapAppBuildingOutline"))[0]; // sId: "__container0"
    
                if (outlineContainer && outlineContainer.getReactProps) {
                    let parseReactState = state => {
                        let components = {};
    
                        let globalState = state.globalState;
                        let instances = globalState.instances;
                        let app = instances.app["[{\"app\":\"MAIN_APPLICATION\"}]"];
                        let names = app.names;
    
                        for (let key in names) {
                            let name = names[key];
    
                            let obj = JSON.parse(key).pop();
                            let type = Object.keys(obj)[0];
                            let id = obj[type];
    
                            components[id] = {
                                type: type,
                                name: name
                            };
                        }
    
                        for (let componentId in components) {
                            let component = components[componentId];
                        }
    
                        let metadata = JSON.stringify({
                            components: components,
                            vars: app.globalVars
                        });
    
                        if (metadata != this.metadata) {
                            this.metadata = metadata;
    
                            this.dispatchEvent(new CustomEvent("propertiesChanged", {
                                detail: {
                                    properties: {
                                        metadata: metadata
                                    }
                                }
                            }));
                        }
                    };
    
                    let subscribeReactStore = store => {
                        this._subscription = store.subscribe({
                            effect: state => {
                                parseReactState(state);
                                return {
                                    result: 1
                                };
                            }
                        });
                    };
    
                    let props = outlineContainer.getReactProps();
                    if (props) {
                        subscribeReactStore(props.store);
                    } else {
                        let oldRenderReactComponent = outlineContainer.renderReactComponent;
                        outlineContainer.renderReactComponent = e => {
                            let props = outlineContainer.getReactProps();
                            subscribeReactStore(props.store);
    
                            oldRenderReactComponent.call(outlineContainer, e);
                        }
                    }
                }
            }
        } catch (e) {}
    }

     

  • disconnectedCallback() It is fired when the widget is removed from the html DOM of the page (e.g. by hide).
    In this function we reset the state of the “react store subscription” subscribeReactStore that we defined in connectedCallback().

    disconnectedCallback() {
        if (this._subscription) { // react store subscription
            this._subscription();
            this._subscription = null;
        }
    }​
  • onCustomWidgetBeforeUpdate() When the custom widget is updated, the Custom Widget SDK framework executes this function first.
    In this function we set the status of _designMode() as in changedProperties.

    onCustomWidgetBeforeUpdate(changedProperties) {
        if ("designMode" in changedProperties) {
            this._designMode = changedProperties["designMode"];
        }
    }
  • onCustomWidgetAfterUpdate() When the custom widget is updated, the Custom Widget SDK framework executes this function after the update.
    In this function we will check if the widget instance loads for the first time. If yes then it will load the quaggaJS library and call the function loadthis().

    onCustomWidgetAfterUpdate(changedProperties) {
        var that = this;
        if (this._firstConnection === 0) {
            this._firstConnection = 1;
            let quaggaminjs = "http://localhost/SAC/sacbarcodescanner/quagga.min.js";
            async function LoadLibs() {
                try {
                    await loadScript(quaggaminjs, _shadowRoot);
                } catch (e) {
                    alert(e);
                } finally {
                    loadthis(that);
                }
            }
            LoadLibs();
        }
    }

 

JavaScript Functions

  • loadthis()
    This function creates the div HTML element to place the UI elements: sap.m.Label, sap.m.Button and sap.m.Input. If is in design mode, the UI elements will be disabled.

    function loadthis(that) {
        var that_ = that;
    
        let buttonSlot = document.createElement('div');
        buttonSlot.slot = "export_button";
        that_.appendChild(buttonSlot);
    
        that_._Label = new sap.m.Label({
            required: false,
            text: "Barcode value",
            design: "Bold"
        });
    
        that_._exportButton = new sap.m.Button({
            id: "scan",
            text: "Scan",
            icon: "sap-icon://bar-code",
            visible: true,
            tooltip: "Scan Barcode",
            press: function() {
                startScan();
            }
        });
    
        that_._Input = new sap.m.Input({
            id: "scannedValue",
            type: sap.m.InputType.Text,
            placeholder: ''
        });
    
        that_._simpleForm = new sap.ui.layout.form.SimpleForm({
            labelSpanL: 3,
            labelSpanM: 3,
            emptySpanL: 3,
            emptySpanM: 3,
            columnsL: 1,
            columnsM: 1,
            editable: true,
            content: [
                that_._Label,
                that_._Input,
                that_._exportButton
            ]
        })
    
        that_._simpleForm.placeAt(buttonSlot);
        that_._renderExportButton();
    
        if (that_._designMode) {
            sap.ui.getCore().byId("scan").setEnabled(false);
            sap.ui.getCore().byId("scannedValue").setEditable(false);
        }
    }​

    In design mode, the UI elements are disabled.

  • _initQuagga()
    Initialize the Quagga library with a given configuration and registers the method  onProcessed() and onDetected().

    function _initQuagga(oTarget, that) {
        var oDeferred = jQuery.Deferred();
    
        // Initialise Quagga plugin - see https://serratus.github.io/quaggaJS/#configobject for details
        Quagga.init({
            inputStream: {
                type: "LiveStream",
                target: oTarget,
                constraints: {
                    width: {
                        min: 640
                    },
                    height: {
                        min: 480
                    },
                    facingMode: "environment"
                }
            },
            locator: {
                patchSize: "medium",
                halfSample: true
            },
            numOfWorkers: 2,
            frequency: 10,
            decoder: {
                readers: [{
                    format: "code_128_reader",
                    config: {}
                }]
            },
            locate: true
        }, function(error) {
            if (error) {
                oDeferred.reject(error);
            } else {
                oDeferred.resolve();
            }
        });
    
        if (!this._bQuaggaEventHandlersAttached) {
            // Attach event handlers...
    
            Quagga.onProcessed(function(result) {
                var drawingCtx = Quagga.canvas.ctx.overlay,
                    drawingCanvas = Quagga.canvas.dom.overlay;
    
                if (result) {
                    // The following will attempt to draw boxes around detected barcodes
                    if (result.boxes) {
                        drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
                        result.boxes.filter(function(box) {
                            return box !== result.box;
                        }).forEach(function(box) {
                            Quagga.ImageDebug.drawPath(box, {
                                x: 0,
                                y: 1
                            }, drawingCtx, {
                                color: "green",
                                lineWidth: 2
                            });
                        });
                    }
    
                    if (result.box) {
                        Quagga.ImageDebug.drawPath(result.box, {
                            x: 0,
                            y: 1
                        }, drawingCtx, {
                            color: "#00F",
                            lineWidth: 2
                        });
                    }
    
                    if (result.codeResult && result.codeResult.code) {
                        Quagga.ImageDebug.drawPath(result.line, {
                            x: 'x',
                            y: 'y'
                        }, drawingCtx, {
                            color: 'red',
                            lineWidth: 3
                        });
                    }
                }
            }.bind(this));
    
            Quagga.onDetected(function(result) {
                // Barcode has been detected, value will be in result.codeResult.code. If requierd, validations can be done 
                // on result.codeResult.code to ensure the correct format/type of barcode value has been picked up
    
                // Set barcode value in input field
                sap.ui.getCore().byId("scannedValue").setValue(result.codeResult.code);
                // Close dialog
                that._oScanDialog.close();
            }.bind(this));
    
            // Set flag so that event handlers are only attached once...
            this._bQuaggaEventHandlersAttached = true;
        }
    
        return oDeferred.promise();
    }
  • startScan()
    This function will be called once user presses the scan button. It creates the dialog box to scan the barcode and initiates the Quagga start() method. The start() method starts the video stream and begins locating and decoding the images.

    function startScan() {
        if (!this._oScanDialog) {
            this._oScanDialog = new sap.m.Dialog({
                title: "Scan Barcode",
                contentWidth: "670px",
                contentHeight: "480px",
                horizontalScrolling: false,
                verticalScrolling: false,
                stretchOnPhone: true,
                content: [new sap.ui.core.HTML({
                    id: "scanContainer",
                    content: "<div />"
                })],
                endButton: new sap.m.Button({
                    text: "Cancel",
                    press: function(oEvent) {
                        this._oScanDialog.close();
                    }.bind(this)
                }),
                afterOpen: function() {
                    // TODO: Investigate why Quagga.init needs to be called every time...possibly because DOM 
                    // element is destroyed each time dialog is closed
                    _initQuagga(sap.ui.getCore().byId("scanContainer").getDomRef(), this).done(function() {
                        // Initialisation done, start Quagga
                        Quagga.start();
                    }).fail(function(oError) {
                        // Failed to initialise, show message and close dialog...this should not happen as we have
                        // already checked for camera device ni /model/models.js and hidden the scan button if none detected
                        MessageBox.error(oError.message.length ? oError.message : ("Failed to initialise Quagga with reason code " + oError.name), {
                            onClose: function() {
                                this._oScanDialog.close();
                            }.bind(this)
                        });
                    }.bind(this));
    
                }.bind(this),
                afterClose: function() {
                    // Dialog closed, stop Quagga
                    Quagga.stop();
                }
            });
        }
    
        this._oScanDialog.open();
    }​
  • loadScript()
    This function loads the external JavaScript library, in this case is quaggaJS.

    function loadScript(src, shadowRoot) {
        return new Promise(function(resolve, reject) {
            let script = document.createElement('script');
            script.src = src;
    
            script.onload = () => {
                console.log("Load: " + src);
                resolve(script);
            }
            script.onerror = () => reject(new Error(`Script load error for ${src}`));
    
            shadowRoot.appendChild(script)
        });
    }
  • createGuid()
    This function is to create the unique ID for HTML div element.

    function createGuid() {
        return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
            let r = Math.random() * 16 | 0,
                v = c === "x" ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

Usage

Upload the Custom Widget

  1. Select Browse and Custom Widgets.
  2. Click + button to upload.
  3. Upload the barcodescanner.json.
  4. Widget is registered and ready to use.

Create and Insert the Widget in Analytics Application

  1. Select Create and Analytics Application.
  2. Insert the widget barcodeScan.
  3. You are ready to go.

 

References:

 

Assigned Tags

      3 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Durairaj Athavan Raja
      Durairaj Athavan Raja

      Thanks for the excellent blog. Have couple of clarifications, hope you can help me.

      1. When you use UI5 controls within the custom widget (shadow dom) how’s the global styles work with your controls inside shadow dom? . I tried with microchart and it didnt work. Finally I had to load the library inside the shadow dom using sap.ui.getCore().loadlibrary so that required library and styles are loaded within the shadow dom to make it work.
      2. Also i see that you are using
        that_._simpleForm = new sap.ui.layout.form.SimpleForm({​

      whereas the sap.ui.layout library is not loaded in app.html . Are you loading dynamically? What is the best way to handle dynamic library load?

      Thanks for you time.

      Regards

      Raja

       

       

       

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi,

      As far as I understand, the UI5 components are loaded as part of the SAC bootstraps process. So you do not need to load them manually. It is dynamically loaded.

      For my case is: https://sapui5.hana.ondemand.com/1.60.13/resources/

      Regards,

      Ferry

      Author's profile photo Alfonso Moreno
      Alfonso Moreno

      Hi Ferry

       

      Sorry, one question. If I want to use UPC/ EAN-13, what a need to modify to read this type of barcode?

       

      Regards!!!