3

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.

2 Answers 2

1

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);
  }
});
Sign up to request clarification or add additional context in comments.

3 Comments

You're welcome. Both should work, in fact. There was a typo, the problem with showGenericModal is that it shouldn't be called.
I think one should be using $injector.invoke() on the first one. I'm no expert on this one though.
@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.
1

@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'
    });
}

12 Comments

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.
BTW, I think your injection on appState_onEnter is wrong; it should be ['AppModals'].
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 How is $uibModal injected in the onEnter function?
@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?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.