Join the Stack Overflow Community
Stack Overflow is a community of 6.7 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I'm using UI Bootstrap's $uibModal to create a modal. I'm also using UI Router 0.2.15, so what I want is a state opening in a new modal.

This is what I have in my config function:

    $stateProvider
    .state("mystate.substate1", {
        url: '...',
        template: '<div ui-view></div>',
        onEnter: showFirstCustomModal
    })
    .state("mystate.substate2", {
        url: '...',
        onEnter: showSecondCustomModal
    });

    // End of calling code

    function showFirstCustomModal($uibModal) {

        var options = {
            backdrop: 'static',
            templateUrl: '...',
            controller: 'Controller1',
            controllerAs: 'controller'
        };

        $uibModal.open(options);
    }

    function showSecondCustomModal($uibModal) {

        var options = {
            backdrop: 'static',
            templateUrl: '...',
            controller: 'Controller2',
        };

        $uibModal.open(options);
    }

The two modal methods above are very similar. I would like to replace them with a generic method:

    $stateProvider
    .state("mystate.substate1", {
        url: '...',
        onEnter: showGenericModal('some_template','SomeController1', 'alias1')
    })
    .state("mystate.substate2", {
        url: '...',
        onEnter: showGenericModal('some_other_template', 'SomeController2')
    });

    // End of calling code

    function showGenericModal(templateUrl, controller, controllerAlias, $uibModal) {

        var options = {
            backdrop: 'static',
            templateUrl: templateUrl,
            controller: controller
        };

        if(!!controllerAlias) {
            options.controllerAs: controllerAlias;
        }

        $uibModal.open(options);
    }

I put the $uibModal as the last argument to avoid it getting reassigned. But I can't get this to work. The error I get is

 Cannot read property 'open' of undefined

Also, I've been reading this and I know that you'll have to use the $injector in order to allow your service to be injected. But I supposed that's already handled by UI-Bootstrap.

share|improve this question
up vote 1 down vote accepted

Since $stateProvider is defined in config block, $uibModal can't be passed from there as a reference.

It is not possible to mix dependencies and normal arguments in Angular DI. For onEnter it should be a function that accepts the list of dependencies.

The code above translates to:

onEnter: showGenericModal('some_other_template', 'SomeController2')

...

function showGenericModal(templateUrl, controller, controllerAlias) {
  return ['$uibModal', function ($uibModal) {
    ...
    $uibModal.open(options);
  }];
}

Or a better approach:

onEnter: function (genericModal) {
  genericModal.show('some_other_template', 'SomeController2');
}

...

app.service('genericModal', function ($uibModal) {
  this.show = function (templateUrl, controller, controllerAlias) {
    ...
    $uibModal.open(options);
  }
});
share|improve this answer
    
I got it to work using the second one. Thanks! – cst1992 Jan 21 at 7:19
    
You're welcome. Both should work, in fact. There was a typo, the problem with showGenericModal is that it shouldn't be called. – estus Jan 21 at 12:13
    
I think one should be using $injector.invoke() on the first one. I'm no expert on this one though. – cst1992 Jan 21 at 12:37
    
@cst1992 onEnter function is called with $injector.invoke under the hood. That's why it is necessary to return another function from showGenericModal(templateUrl, controller, controllerAlias) call, like it was done in the first snippet. – estus Jan 21 at 12:46

@estus answer correct, I don't know how I didn't saw the state: "For onEnter it should be a function that accepts the list of dependencies.".

However, I will let my answer here to provide another perspective. You can define a service to wrap up and organize correctly your code, in order to call a customized modal on onEnter state event:

angular.module('app').service('AppModals', AppModals);

// or use /** @ngInject */ aswell
AppModals.$inject = ['$uibModal'];

function AppModals($uibModal) {
    this.open = function _generateModal(options) {
        var defaultOptions = {
            backdrop: 'static'
            // Any other default option
        };

        return $uibModal.open(angular.extend({}, defaultOptions, options);
    };
}

On the state definition:

$stateProvider
    .state('app.state', {
        url: '/state-modal',
        template: '<ui-view></ui-view>',
        controller: 'DummyCtrl',
        controllerAs: 'dummy',
        onEnter: appState_onEnter
    });

// or use /** @ngInject */ aswell
appState_onEnter.$inject = ['$uibModal'];

function appState_onEnter(AppModals) {
    AppModals.open({
        templateUrl: 'modals/state-modal.html',
        controller: 'DummyCtrl',
        controllerAs: 'dummy'
    });
}
share|improve this answer
    
I think @estus means that it is not possible to mix and match injectable and non-injectable arguments in Angular DI. If a service is created, it is made injectable, so that it can then be used as an argument to the onEnter function. – cst1992 Jan 21 at 6:20
    
BTW, I think your injection on appState_onEnter is wrong; it should be ['AppModals']. – cst1992 Jan 21 at 7:21
    
It wasn't erroneous information. DI isn't used here showGenericModal('some_other_template', 'SomeController2'), the function is just called from parent function (config block), with $uibModal param being omitted. That's why it doesn't cause injection error. If it were some other function scope, it would be possible to pass $uibModal from there, but it's not the case for $stateProvider. – estus Jan 21 at 12:06
    
@estus How is $uibModal injected in the onEnter function? – cst1992 Jan 21 at 12:39
    
@estus is an erroneous information, you are not able to see in your code that you define a function to simply return an annotated array? If you declare the array directly the result is the same. From their docs "Callback function for when a state is entered. Good way to trigger an action or dispatch an event, such as opening a dialog. If minifying your scripts, make sure to explicitly annotate this function, because it won't be automatically annotated by your build tools.". Is it clear now? – Mateus Duarte Ponce de Leon Jan 23 at 13:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.