Link to home
Start Free TrialLog in
Avatar of hostingplus
hostingplus

asked on

HTML Canvas error: 'getContext' of null - in AngularJS App

Hi EE!

We use have been using a HTML canvas to print out orders and 99% of the time it works fine. but randomly...

User generated image
Its like it has lost the canvas ID, but was working fine. Below is our angularJS orderPrinterFactory.js code that writes the canvas each print (which can be multiple as once)

We are really stuck with this one! Any help would be great!

(function () {

    var injectParams = ['$rootScope', '$http', 'ngDexie', '$q', 'tabFactory'];

    var orderPrinterFactory = function ($rootScope, $http, ngDexie, $q, tabFactory) {
                
        function drawReceipt(canvas, receiptData, products, note) {
            // get context of canvas
            $rootScope.consolelog(receiptData)
            //if (canvas.getContext) {
                if(canvas === null){
                     canvas = document.getElementById('orderCanvas');
                }
                var context = canvas.getContext('2d');
                canvas.height = '20000';
                var fontType = '\'Arial\', sans-serif';
                var x = parseInt('0');
                var y = parseInt('30');
                var titleFont = 'normal ' + 'normal ' + 'bold ' + 40 + 'px ' + fontType; //(true or false) | italics (italic or normal), caps (small-caps or normal), bold (bold or normal), size, font
                var tabFont = 'normal ' + 'normal ' + 'bold ' + 78 + 'px ' + fontType; //(true or false) | italics (italic or normal), caps (small-caps or normal), bold (bold or normal), size, font
                var subTitleFont = 'normal ' + 'normal ' + 'normal ' + 36 + 'px ' + fontType; //(true or false) | italics (italic or normal), caps (small-caps or normal), bold (bold or normal), size, font
                var normalFont = 'normal ' + 'normal ' + 'normal ' + 36 + 'px ' + fontType; //(true or false) | italics (italic or normal), caps (small-caps or normal), bold (bold or normal), size, font
                var normalBoldFont = 'normal ' + 'normal ' + 'bold ' + 36 + 'px ' + fontType; //(true or false) | italics (italic or normal), caps (small-caps or normal), bold (bold or normal), size, font
                var smallFont = 'normal ' + 'normal ' + 'normal ' + 24 + 'px ' + fontType; //(true or false) | italics (italic or normal), caps (small-caps or normal), bold (bold or normal), size, font
                var rWidth = parseInt('576');
                var smallLine = parseInt('15');
                var line = parseInt('30');
                var centerX = parseInt('288');
                var breakLine = '----------------------------------------------------------------------';
                var currentTime = new Date()
                var seconds = currentTime.getSeconds()
                if (seconds < 10) {
                    seconds = "0" + seconds
                }
                var minutes = currentTime.getMinutes()
                if (minutes < 10) {
                    minutes = "0" + minutes
                }
                var hours = currentTime.getHours()
                if (hours > 11) {
                    var timeFormat = "PM";
                } else {
                    var timeFormat = "AM";
                }
                if (hours > 12) {
                    hours = hours - 12
                }
                var month = currentTime.getMonth() + 1
                if (month < 10) {
                    month = "0" + month
                }
                var day = currentTime.getDate()
                var year = currentTime.getFullYear()
                var date = day + "/" + month + "/" + year;
                var time = hours + ":" + minutes + ":" + seconds + ' ' + timeFormat;
                
                y = y + line;
                if (receiptData.transaction.tab.tabNumber) {
                    context.textAlign = 'center';
                    context.font = tabFont;
                    context.textBaseline = 'alphabetic';
                    context.fillText('Tab ' + receiptData.transaction.tab.tabNumber, centerX, y);
                    y = y + line + line;
                }
                if (receiptData.transaction.tab.tableNumber) {
                    context.textAlign = 'center';
                    context.font = tabFont;
                    context.textBaseline = 'alphabetic';
                    context.fillText('Table ' + receiptData.transaction.tab.tableNumber, centerX, y);
                    y = y + line + line;
                }
                
                context.textAlign = 'center';
                context.font = smallFont;
                context.textBaseline = 'alphabetic';
                context.fillText('STAFF MEMBER: ' + receiptData.staff.toUpperCase(), centerX, y);
                y = y + line;

                context.textAlign = 'center';
                context.font = subTitleFont;
                context.textBaseline = 'alphabetic';
                context.fillText(breakLine, centerX, y);
                y = y + line + line;

                context.textAlign = 'start';
                context.font = normalFont;
                context.textBaseline = 'alphabetic';
                context.fillText(date, 0, y);
                context.textAlign = 'end';

                context.font = normalFont;
                context.textBaseline = 'alphabetic';
                context.fillText(time, rWidth, y);
                y = y + line + line;

                context.textAlign = 'start';
                context.font = normalBoldFont;
                context.textBaseline = 'alphabetic';
                context.fillText('PRODUCTS', 0, y);
                y = y + line + smallLine;

                $rootScope.consolelog(products)

                for (var i = 0; i < products.length; i++) {

                    context.textAlign = 'start';
                    context.font = normalFont;
                    context.textBaseline = 'alphabetic';
                    context.fillText(products[i].quantity + ' x ' + products[i].buttonName, 0, y);

                    if(products[i].modifiers) {
                        
                        y = y + line;
                        
                        for (var m = 0; m < products[i].modifiers.length; m++) {

                            context.textAlign = 'start';
                            context.font = smallFont;
                            context.textBaseline = 'alphabetic';
                            context.fillText('[M] ' + products[i].modifiers[m].name, 60, y);

                            y = y + line;

                            if(products[i].subModifiers) {
                                for (var s = 0; s < products[i].subModifiers.length; s++) {
                                    if(products[i].subModifiers[s].parent === products[i].modifiers[m].id){
                                        context.textAlign = 'start';
                                        context.font = smallFont;
                                        context.textBaseline = 'alphabetic';
                                        context.fillText('- ' + products[i].subModifiers[s].name, 100, y);

                                        y = y + line;

                                        if(products[i].subSubModifiers) {
                                            for (var ss = 0; ss < products[i].subSubModifiers.length; ss++) {
                                                if(products[i].subSubModifiers[ss].parent === products[i].subModifiers[s].id){
                                                    context.textAlign = 'start';
                                                    context.font = smallFont;
                                                    context.textBaseline = 'alphabetic';
                                                    context.fillText('- ' + products[i].subSubModifiers[ss].name, 140, y);

                                                    y = y + line;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    y = y + line + smallLine;

                }
                
                if(note != ''){
                    context.textAlign = 'start';
                    context.font = normalBoldFont;
                    context.textBaseline = 'alphabetic';
                    context.fillText('NOTE', 0, y);
                    y = y + line + smallLine;

                    context.textAlign = 'start';
                    context.font = normalFont;
                    context.textBaseline = 'alphabetic';
                    context.fillText(note, 0, y);
                    y = y + line + smallLine;
                }
                
                var imgData = context.getImageData(0, 0, rWidth, y);
                canvas.height = y;
                context.putImageData(imgData, 0, 0);

                return y;
            //}

        };

        function lookup(printQueueId, productToPrintQueue) {
            for (var i = 0, len = productToPrintQueue.length; i < len; i++) {
                if (productToPrintQueue[i].pqid === printQueueId)
                    return true;
            }
            return false;
        }

        var orderPrinter = {};

        // print canvas
        orderPrinter.orderPrintCanvas = function (receiptData, note) {
            var duplicatePrinters = []
            var productToPrintQueue = []
            angular.forEach(receiptData.transaction.products, function (value) { //loop through transaction products
                $rootScope.consolelog(value)
                ngDexie.get('products', value.id).then(function (product) { //foreach product get that products information from indexeddb
                    $rootScope.consolelog(product)
                    var printQueueId = product.printQueueId;
                    if (lookup(printQueueId, productToPrintQueue)) {
                        for (var i = 0, l = productToPrintQueue.length; i < l; i++) {
                            if (productToPrintQueue[i].pqid === printQueueId) {
                                productToPrintQueue[i].products.push({
                                    id: product.id,
                                    quantity: value.quantity,
                                    name: product.name,
                                    buttonName: product.buttonName,
                                    modifiers: value.modifiers,
                                    subModifiers: value.subModifiers,
                                    subSubModifiers: value.subSubModifiers
                                })
                            } else {
                                productToPrintQueue.push({
                                    pqid: printQueueId,
                                    products: [{
                                        id: product.id,
                                        quantity: value.quantity,
                                        name: product.name,
                                        buttonName: product.buttonName,
                                        modifiers: value.modifiers,
                                        subModifiers: value.subModifiers,
                                        subSubModifiers: value.subSubModifiers
                                    }]
                                })
                            }
                        }
                    } else {
                        productToPrintQueue.push({
                            pqid: printQueueId,
                            products: [{
                                id: product.id,
                                quantity: value.quantity,
                                name: product.name,
                                buttonName: product.buttonName,
                                modifiers: value.modifiers,
                                subModifiers: value.subModifiers,
                                subSubModifiers: value.subSubModifiers
                            }]
                        })
                    }
                }).finally(function () {
                    //$rootScope.consolelog(productToPrintQueue)
                    angular.forEach(productToPrintQueue, function (printQueue) { 
                        ngDexie.getByIndex('localprinters', 'printQueueId', parseInt(printQueue.pqid)).then(function (data) { //then from the products store get the printqueueid for that particular product
                            if(data.ip != '0.0.0.0'){
                                 if (duplicatePrinters.indexOf(printQueue.pqid) < 0) { // check if the the printer ip is already in the array then dont print as we already have :)
                                    $rootScope.consolelog('products to be printed: ' + printQueue.products);
                                    var canvas = document.getElementById('orderCanvas');
                                    var y = drawReceipt(canvas, receiptData, printQueue.products, note);
                                    var address = 'http://' + data.ip + '/cgi-bin/epos/service.cgi?devid=local_printer&timeout=60000';
                                    var context = canvas.getContext('2d');
                                    var builder = new epson.ePOSBuilder();
                                    builder.brightness = 1.0;
                                    builder.halftone = builder.HALFTONE_ERROR_DIFFUSION;
                                    // builder.addLayout(builder.LAYOUT_RECEIPT, 800, 0, 0, 0, 0, 0)
                                    builder.addImage(context, 0, 0, 576, y, builder.COLOR_1, builder.MODE_MONO);
                                    builder.addCut(builder.CUT_FEED);

                                    var epos = new epson.ePOSPrint(address);
                                    //epos.onreceive = function (res) { alert(res.success); };
                                    epos.onerror = function (err) {
                                        $rootScope.consolelog(err.status);
                                    };
                                    //epos.oncoveropen = function () { alert('coveropen'); };

                                    epos.send(builder.toString());

                                    //epos.print(canvas);
                                    $rootScope.consolelog('reciept printed');
                                    canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
                                    duplicatePrinters.push(printQueue.pqid)
                                    //$rootScope.consolelog(duplicatePrinters)
                                }
                            }
                        })
                    });
                })
            })
        };
        
        orderPrinter.orderPrintRemainingCanvas = function (receiptData, note) {
            var duplicatePrinters = []
            var productToPrintQueue = []
            tabFactory.getProductsToPrintRemaining(receiptData.transaction.tab.tabId).success(function (transactionProducts){
                $rootScope.consolelog('transactionProducts: ' + transactionProducts)
                angular.forEach(transactionProducts, function (value) { //loop through transaction products
                    $rootScope.consolelog(value);
                    ngDexie.get('products', value.productId).then(function (product) { //foreach product get that products information from indexeddb
                        $rootScope.consolelog(product)
                        var printQueueId = product.printQueueId;
                        if (lookup(printQueueId, productToPrintQueue)) {
                            for (var i = 0, l = productToPrintQueue.length; i < l; i++) {
                                if (productToPrintQueue[i].pqid === printQueueId) {
                                    if(value.quantity === value.printed){
                                        var printedQty = '0';
                                    }else{
                                        var printedQty = parseInt(value.quantity) - parseInt(value.printed);
                                    }
                                    $rootScope.consolelog(printedQty)
                                    if(printedQty != '0') {
                                        productToPrintQueue[i].products.push({
                                            id: product.id,
                                            quantity: printedQty,
                                            name: product.name,
                                            buttonName: product.buttonName,
                                            modifiers: value.modifiers,
                                            subModifiers: value.subModifiers,
                                            subSubModifiers: value.subSubModifiers
                                        })
                                    }

                                } else {
                                    if(value.quantity === value.printed){
                                        var printedQty = '0';
                                    }else{
                                        var printedQty = parseInt(value.quantity) - parseInt(value.printed);
                                    }
                                    $rootScope.consolelog(printedQty)
                                    if(printedQty != '0') {
                                        productToPrintQueue.push({
                                            pqid: printQueueId,
                                            products: [{
                                                id: product.id,
                                                quantity: printedQty,
                                                name: product.name,
                                                buttonName: product.buttonName,
                                                modifiers: value.modifiers,
                                                subModifiers: value.subModifiers,
                                                subSubModifiers: value.subSubModifiers
                                            }]
                                        })
                                    }
                                }
                            }
                        } else {
                            if(value.quantity === value.printed){
                                var printedQty = '0';
                            }else{
                                var printedQty = parseInt(value.quantity) - parseInt(value.printed);
                            }
                            $rootScope.consolelog(printedQty)
                            if(printedQty != '0') {
                                productToPrintQueue.push({
                                    pqid: printQueueId,
                                    products: [{
                                        id: product.id,
                                        quantity: printedQty,
                                        name: product.name,
                                        buttonName: product.buttonName,
                                        modifiers: value.modifiers,
                                        subModifiers: value.subModifiers,
                                        subSubModifiers: value.subSubModifiers
                                    }]
                                })
                            }
                        }
                        tabFactory.setProductsToPrintRemaining(receiptData.transaction.tab.tabId, value.randomId);
                    }).finally(function () {
                        //$rootScope.consolelog(productToPrintQueue)
                        angular.forEach(productToPrintQueue, function (printQueue) { 
                            ngDexie.getByIndex('localprinters', 'printQueueId', parseInt(printQueue.pqid)).then(function (data) { //then from the products store get the printqueueid for that particular product
                                if(data.ip != '0.0.0.0'){
                                     if (duplicatePrinters.indexOf(printQueue.pqid) < 0) { // check if the the printer ip is already in the array then dont print as we already have :)
                                        $rootScope.consolelog('products to be printed: ');
                                        $rootScope.consolelog(printQueue.products);
                                        var canvas = document.getElementById('orderCanvas');
                                        var y = drawReceipt(canvas, receiptData, printQueue.products, note);
                                        var address = 'http://' + data.ip + '/cgi-bin/epos/service.cgi?devid=local_printer&timeout=60000';
                                        var context = canvas.getContext('2d');
                                        var builder = new epson.ePOSBuilder();
                                        builder.brightness = 1.0;
                                        builder.halftone = builder.HALFTONE_ERROR_DIFFUSION;
                                        // builder.addLayout(builder.LAYOUT_RECEIPT, 800, 0, 0, 0, 0, 0)
                                        builder.addImage(context, 0, 0, 576, y, builder.COLOR_1, builder.MODE_MONO);
                                        builder.addCut(builder.CUT_FEED);

                                        var epos = new epson.ePOSPrint(address);
                                        //epos.onreceive = function (res) { alert(res.success); };
                                        epos.onerror = function (err) {
                                            $rootScope.consolelog(err.status);
                                        };
                                        //epos.oncoveropen = function () { alert('coveropen'); };

                                        epos.send(builder.toString());

                                        //epos.print(canvas);
                                        $rootScope.consolelog('reciept printed');
                                        canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
                                        duplicatePrinters.push(printQueue.pqid)
                                        //$rootScope.consolelog(duplicatePrinters)
                                    }
                                }
                            })
                        });
                    })
                })
            })
        };
        return orderPrinter;

    }

    orderPrinterFactory.$inject = injectParams;

    angular.module('myApp').factory('orderPrinterFactory', orderPrinterFactory);

}());

Open in new window

Avatar of BigRat
BigRat
Flag of France image

I presume the problem arises from :-

                if(canvas === null){
                     canvas = document.getElementById('orderCanvas');
                }
                var context = canvas.getContext('2d');

and I'd ask myself if in fact canvas is null? or undefined? or whatever by simply removing the test.
Furthermore I'd double check that "orderCanvas" is defined as an element by logging an error after the getElementById.
Avatar of hostingplus
hostingplus

ASKER

Hi BigRat,

Many thanks for commenting, your help on this issue would be great. Just as a side note also, we would be happy to pay for some assistance with this - We could arrange a skype/teamviewer session if its easier for you but ill provide more information now.

 if(canvas === null){
                     canvas = document.getElementById('orderCanvas');
                }
                var context = canvas.getContext('2d');

Open in new window


The above is a catch we have only recently included,hoping if is was NULL it would recreate it. Dosnt seem to be the case.

canvas = document.getElementById('orderCanvas');
var context = canvas.getContext('2d');

Open in new window


We have caught the error at a pilot site and can confirm when we inspected that "orderCanvas" was defined as a element on the page, but this error continued to come up until we refreshed.

It was alike the canvas was "broken" or it would not see it.

Im sure you can understand its a very difficult issue to explain, but let me know if I can provide anymore details or information.
Quick question : with which browser does this error occur?
We only run and support Chrome at this stage. Latest version or close to. Thanks
While I think about that, you could try to get someone to press the F12 key when the error occurs just to see if there are any other errors occuring. I would also be a lot happier if you tried to detect if "canvas" was null (as if it could not be found), log it, alert it (or display an error) and restarted/reloaded the app.

With errors like this, one ought to switch to a more permanent loggin (not console but in a file) and start logging events and calls that have "something to do with the error". Like for example, is "document"in this context the correct "document", for that would cause the canvas element not to be found.
Thanks BigRat,

Clients have called us when this has occurred and we jumped straight on remotely and can confirm the only error is that seen in the screen shot above (from that client even)

We are not sure how we get back NULL sometimes as the spec stats URL HERE We dont use WebGL at all.

context = canvas . getContext(contextId [, ... ] )
Returns an object that exposes an API for drawing on the canvas. The first argument specifies the desired API, either "2d" or "webgl". Subsequent arguments are handled by that API.

This specification defines the "2d" context below. There is also a specification that defines a "webgl" context. [WEBGL]

Returns null if the given context ID is not supported, if the canvas has already been initialised with the other context type (e.g. trying to get a "2d" context after getting a "webgl" context).

Throws an InvalidStateError exception if the setContext() or transferControlToProxy() methods have been used.
Also wanted to mention,

this is our canvas from the web-app:

<canvas id="orderCanvas" width="576" height="1500" style="display:none;"></canvas>

Open in new window


Lets say someone orders 3 drinks and 2 Steaks.

They will print at different printers:

3 drinks will be printed on the drinks printer (192.168.1.1)
2 steaks will be printed to the food printer (192.168.1.2)

As you will see the orderPrinterFactory.js is what does this, but they both use the same canvas - this could be an issue but for days ive been replicating that in house and it never fails! Big orders all types. Just works.
ASKER CERTIFIED SOLUTION
Avatar of BigRat
BigRat
Flag of France image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi BigRat,

I've implemented what you have suggested by adding a hidden input and in our factory we get the documentById and then change the value. Will see if we get the same error.

Another thought as JS runs line by line but does not wait for the previous line to finish before executing the next line could this be our problem? So the getContext is executing before the var canvas is set?

Thanks
No, JS executes synchronously. In fact in IE JS executes on the same thread as the GUI, so the screen does not get updated until the script has finished.

I suspect that, under certain circumstances, the "document" variable end up not pointing to the document which contains the canvas.