Skip to Content
Technical Articles

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:

 

2 Comments
You must be Logged on to comment or reply to a post.
  • 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