4

I have a service that contains a function, that shows a bootstrap modal. The modals result promise, when closing, is received inside the service and a promise is then resolved to the caller of the function. The service function itself, returns a promise.

My problem is, that the controller never receives the resolved promise, after the modal closes. The modals result promise, where i resolve my promise, gets hit, and commentCount is the right value.

I'm new to the whole promise thing, so this might not be the correct way to do it, but shouldn't it work as it is now?

EDIT:

I'm not just returning the promise from instance.result, because i need to do something else inside the service, before returning the commentCount to the caller of the function. This is not implemented yet, though.

service:

function postModal($http, $rootScope, $uibModal, userService, utilService, enumService, $q) {
        var service = {};      

        service.showModal = function (postId, category) {
            var deferred = $q.defer();

            var extraClass = (category == enumService.postType.ARTICLE) ? 'article-post' : '';
            var instance = $uibModal.open({
                templateUrl: 'app/views/post_modal.html',
                controller: 'postController',
                controllerAs: 'postController',
                windowClass: 'center-modal post-modal',
                backdropClass: 'post-backdrop ' + extraClass,
                background: 'static',
                resolve: {
                    postId: function () {
                        return postId;
                    },
                    category: function () {
                        return category;
                    },
                    modalInstance: function () {
                        return this;
                    }
                }
            });     

            instance.result.then(function (commentCount) { 
                deferred.resolve(commentCount);
            });

            return deferred.promise;
        };

        return service;
    }

Code from controller:

service.showModal(postId, category)
.then(function (commentCount) {
     var comments = commentCount;
});
Teilmann
  • 2,146
  • 7
  • 28
  • 57
  • If instead of using `$q.deferred` you just return `instance.result` (I know you said you want to do more stuff, but just for the sake of debugging), do you get the promise resolved in the controller? – Yaron Schwimmer May 06 '16 at 11:15
  • Yes, i tried that, and it worked. :) I actually ended up doing something completely different, but for the sake of knowing, i'm gonna let the question hang a bit. – Teilmann May 06 '16 at 11:28
  • 1
    Avoid the [deferred antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi May 06 '16 at 12:39
  • Some suggestions: 1. You can inject $uibModalInstance into modal controller, so there is no need for modalInstance in resolve. 2. Promise then always returns promise so just something like that http://pastebin.com/N2Mpzz5F – sielakos May 21 '16 at 07:32
  • How did you check that your `than`'s function in controller were not called? Because in case of working with returning `instance.result`, it should work correctly with presented code. – atn Aug 15 '16 at 14:00

1 Answers1

0

I'm not sure will this help you, but it will definitely help someone who wants to have a bootstrap modal instead of browsers default. I've made a service and controller which depends of eachother:

.service('AlertService', function($uibModal){
    /*
        headerText - presents text in header
        bodyText - presents text in body
        buttonText - presents text in button. On its click if method parameters is not passed, modal will be closed.
                    In situation that the method parameters is passed, on its click, method will be called. For situations
                    like that, there is parameter buttonText2 which will be used as cancel modal functionality.
        method - presents passed function which will be called on confirmation
        buttonText2 - presents text in button for cancel

     */
    var alert = function(headerText, bodyText, buttonText, method, buttonText2){

        method = method || function(){};
        buttonText2 = buttonText2 || '';

        $uibModal.open({
            animation: true,
            templateUrl: '/static/angular_templates/alert-modal.html',
            controller: 'AlertModalInstanceCtrl',
            size: 'md',
            resolve: {
                headerText: function () {
                  return headerText;
                },
                bodyText: function () {
                  return bodyText;
                },
                buttonText: function () {
                  return buttonText;
                },
                method: function () {
                    return method;
                },
                buttonText2: function () {
                    return buttonText2;
                }
            }
        });
    };

    return{
        alert: alert
    };

})
.controller('AlertModalInstanceCtrl', function ($scope, $uibModalInstance, headerText, bodyText, buttonText, method, buttonText2) {
    $scope.headerText = headerText;
    $scope.bodyText = bodyText;
    $scope.buttonText = buttonText;
    $scope.method = method;
    $scope.buttonText2 = buttonText2;

    $scope.ok = function () {
        $scope.method();
        $uibModalInstance.dismiss('cancel');
    };

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

and html file:

<!--Modal used for alerts in AlertService-->

<div class="modal-header">
    <h3 class="modal-title">{[{ headerText }]}</h3>
</div>
<div class="modal-body">
    <p>{[{ bodyText }]}</p>
</div>
<div class="modal-footer">
    <button class="btn btn-default" ng-click="cancel()" ng-if="buttonText2">{[{ buttonText2 }]}</button>
    <button class="btn btn-primary" ng-click="ok()">{[{ buttonText }]}</button>
</div>

Now, depending for what type you want to use it, you have a few options: -If you pass headerText, bodyText and buttonText, it will behave like a classic alert modal

AlertService.alert('Some header', 'Some message', 'Text button');

-If you pass headerText, bodyText, buttonText and method, it will behave like a classic alert modal but with the function which you can pass and later handle in the controller

AlertService.alert('Are you sure?', 'Are you sure you want to create this round', 'Ok', $scope.createRound);

$scope.createRound = function(){
//do something
}

-And the last one. If you pass all the parameters, it will act like the previous one, just with the possibility to cancel and close modal.

AlertService.alert('Are you sure?', 'Are you sure you want to create this round', 'Ok', $scope.createRound, 'Cancel');

$scope.createRound = function(){
//do something
}

Of course, if you want to use this, you'll have to inject angular ui bootstrap. I wasted a lot of time to develop this, but it worth. It was annoying to create every time a new controller, new template and all the other things.

From the controller then you can easily use it, just inject it first.

Igor Janković
  • 5,494
  • 6
  • 32
  • 46