2

I have a function inside my controller that is called from a third party library requiring a boolean response. The function opens a modal (which is defined as a service) to get the response from the user and returns this as a promise. However, when dismiss is pressed on the modal I get an error in the console

Possibly unhandled rejection: cancel

I have spent hours trying to fix this with various methods but cannot remove the error whilst returning the true/false. Can anyone help?

Service

angular.module('app')
.service("ModalService", function ($uibModal) {

var modalDefaults = {
    backdrop: true,
    keyboard: true,
    modalFade: true,
    templateUrl: '/app/components/shared/modal.html'
};

var modalOptions = {
    closeButtonText: 'No',
    actionButtonText: 'Yes',
    headerText: 'Title',
    bodyText: 'Are you sure you wish to do this?'
};

this.showModal = function (customModalDefaults, customModalOptions) {
    if (!customModalDefaults) {
        customModalDefaults = {};
    }
    customModalDefaults.backdrop = 'static';
    return this.show(customModalDefaults, customModalOptions);
};

this.show = function (customModalDefaults, customModalOptions) {
    // Create temp objects to work with since we're in a singleton service
    var tempModalDefaults = {};
    var tempModalOptions = {};

    // Map angular-ui modal custom defaults to modal defaults defined in service
    angular.extend(tempModalDefaults, modalDefaults, customModalDefaults);

    // Map modal.html $scope custom properties to defaults defined in service
    angular.extend(tempModalOptions, modalOptions, customModalOptions);

    if (!tempModalDefaults.controller) {
        tempModalDefaults.controller = function ($scope, $uibModalInstance) {

            $scope.modalOptions = tempModalOptions;

            $scope.modalOptions.ok = function (result) {
                $uibModalInstance.close(true);
            };

            $scope.modalOptions.close = function (result) {
                $uibModalInstance.dismiss('cancel');
            };
        }
    }

    return $uibModal.open(tempModalDefaults).result;
};

});

Controller Function

this.onRemovingLabel = function($tag) { 
    var modalOptions = {
        closeButtonText: 'Cancel',
        actionButtonText: 'Delete',
        headerText: 'Delete Label: ' + $tag.name,
        bodyText: 'By removing this label all marked tags in the the dataset will be deleted. Do you wish to continue?'
    };

    return ModalService.showModal({}, modalOptions);
}

Function is called from HTML

<tags-input ... on-tag-removing="tc.onRemovingLabel($tag)">
Carl
  • 841
  • 1
  • 13
  • 33

2 Answers2

0

To avoid using a deferred anti-pattern, simply return the modified promise:

this.onRemovingLabel = function($tag) { 
        var modalOptions = {
            closeButtonText: 'Cancel',
            actionButtonText: 'Delete',
            headerText: 'Delete Label: ' + $tag.name,
            bodyText: 'By removing this label all marked tags in the the dataset will be deleted. Do you wish to continue?'
        };

        ̶v̶a̶r̶ ̶d̶e̶f̶e̶r̶ ̶=̶ ̶$̶q̶.̶d̶e̶f̶e̶r̶(̶)̶;̶ 

        ̲r̲e̲t̲u̲r̲n̲ ModalService.showModal({}, modalOptions) ̶.̶t̶h̶e̶n̶(̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶(̶r̶e̶s̶u̶l̶t̶)̶ ̶{̶    
            ̶d̶e̶f̶e̶r̶.̶r̶e̶s̶o̶l̶v̶e̶(̶r̶e̶s̶u̶l̶t̶)̶;̶
        ̶}̶)̶ .catch(function(result) {
            // Modal cancelled, this prevents an unhandled exception error on the console.
        });

        ̶r̶e̶t̶u̶r̶n̶ ̶d̶e̶f̶e̶r̶.̶p̶r̶o̶m̶i̶s̶e̶;̶
}

When a .catch handler function omits a return statement, it returns a value of undefined which will convert the rejected promise to a one that settles successfully with a value of undefined.

OR

return ModalService.showModal({}, modalOptions)
  .catch(function(result) {
    return false;
});

This will convert a rejected promise to a sucessful promise that returns the result.

For more information, see You're Missing the Point of Promises.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • Thanks @georgeawg. I can't quite make out the code you've modified, are you saying to catch and then inside the catch defer.resolve(result)? – Carl Feb 09 '18 at 08:37
  • This returns cancel and not the false that the 3rd party library requires. – Carl Feb 10 '18 at 13:46
-1

I fixed this eventually with

this.onRemovingLabel = function($tag) { 
        var modalOptions = {
            closeButtonText: 'Cancel',
            actionButtonText: 'Delete',
            headerText: 'Delete Label: ' + $tag.name,
            bodyText: 'By removing this label all marked tags in the the dataset will be deleted. Do you wish to continue?'
        };

        var defer = $q.defer();

        ModalService.showModal({}, modalOptions).then(function (result) {
            defer.resolve(result);
        }).catch(function(result) {
            // Modal cancelled, this prevents an unhandled exception error on the console.
        });

        return defer.promise;
}
Carl
  • 841
  • 1
  • 13
  • 33
  • This is a [deferred anti-pattern](https://stackoverflow.com/questions/30750207/is-this-a-deferred-antipattern) that hangs and creates memory leaks when the `showModal` function returns a rejected promise. – georgeawg Feb 08 '18 at 16:21