8

I am trying to do what was essentially answered here Unable to open bootstrap modal window as a route

Yet my solution just will not work. I get an error

Error: [$injector:unpr] Unknown provider: $modalProvider <- $modal

My app has the ui.bootstrap module injected - here is my application config

var app = angular.module('app', ['ui.router', 'ui.bootstrap','ui.bootstrap.tpls', 'app.filters', 'app.services', 'app.directives', 'app.controllers'])

    // Gets executed during the provider registrations and configuration phase. Only providers and constants can be
    // injected here. This is to prevent accidental instantiation of services before they have been fully configured.
    .config(['$stateProvider', '$locationProvider', function ($stateProvider, $locationProvider) {

        // UI States, URL Routing & Mapping. For more info see: https://github.com/angular-ui/ui-router
        // ------------------------------------------------------------------------------------------------------------   

        $stateProvider
            .state('home', {
                url: '/',
                templateUrl: '/views/index',
                controller: 'HomeCtrl'

            })
            .state('transactions', {
                url: '/transactions',
                templateUrl: '/views/transactions',
                controller: 'TransactionsCtrl'
            })      
            .state('login', {
                url: "/login",
                templateUrl: '/views/login',
                controller: 'LoginCtrl'                
            })

            .state('otherwise', {
                url: '*path',
                templateUrl: '/views/404',
                controller: 'Error404Ctrl'
            });

        $locationProvider.html5Mode(true);

    }])

I have reduced my controller to the following:

appControllers.controller('LoginCtrl', ['$scope', '$modal', function($scope, $modal) {
    $modal.open({templateUrl:'modal.html'});
}]);

Ultimately, what I am hoping to achieve is when login is required not actually GO to the login page, but bring up a dialog.

I have also tried using the onEnter function in the ui-router state method. Couldn't get this working either.

Any ideas?

UPDATE

Ok - so as it turns out, having both ui-bootstrap.js AND ui-bootstrap-tpls breaks this - After reading the docs I thought you needed the templates to work WITH the ui-bootstrap. though it seems all the plunkers only load in the ..tpls file - once I removed the ui-bootstrap file my modal works...Am i blind? or doesn't it not really say which one you need in the docs on github? -

Now i just need to figure out how to prevent my url from actually going to /login, rather than just show the modal :)

update 2

Ok, so by calling $state.go('login') in a service does this for me.

11
  • Your controller shouldn't need '$modal' as a dependency as you loaded 'ui.bootstrap' in your app dependencies. Could you try removing it? (But leave the $modal in the controller function parameter) ;) Commented Feb 19, 2014 at 14:39
  • nope - now i get ReferenceError: $modal is not defined Commented Feb 19, 2014 at 14:40
  • Also checked to make sure that angular-ui-bootstrap.js is loaded after angular.js, and it is Commented Feb 19, 2014 at 14:42
  • Ok, and isn't 'ui.bootstrap.modal' a separate module from 'ui.bootstrap'? Maybe try loading it into your app or controller dependencies. I only use the modal from angular-ui-bootstrap and that's the way I load it. Commented Feb 19, 2014 at 14:47
  • I have the full js - least I believe i do - it contains this line: .provider('$modal', function () { Commented Feb 19, 2014 at 14:48

4 Answers 4

11

Hi I had a hard time getting through the similar problem. However, I was able to resolve it. This is what you would probably need.

app.config(function($stateProvider) {
  $stateProvider.state("managerState", {
      url: "/ManagerRecord",
      controller: "myController",
      templateUrl: 'index.html'
    })
    .state("employeeState", {
      url: "empRecords",
      parent: "managerState",
      params: {
        empId: 0
      },
      onEnter: [
        "$modal",
        function($modal) {
          $modal.open({
            controller: "EmpDetailsController",
            controllerAs: "empDetails",
            templateUrl: 'empDetails.html',
            size: 'sm'
          }).result.finally(function() {
            $stateProvider.go('^');
          });
        }
      ]
    });
});

Click here for plunker. Hope it helps.

Sign up to request clarification or add additional context in comments.

1 Comment

This does not seem to close the dialog when pressing back though
5

I'm working on something similar and this is my solution.

HTML code

<a ui-sref="home.modal({path: 'login'})" class="btn btn-default" ng-click="openModal()">Login</a>

State configuration

$stateProvider
  // assuming we want to open the modal on home page
  .state('home', {
    url: '/',
    templateUrl: '/views/index',
    controller: 'HomeCtrl'
  })
  // create a nested state
  .state('home.modal', {
    url: ':path/'
  });

Home controller

//... other code
$scope.openModal = function(){
  $modal.open({
    templateUrl: 'path/to/page.html',
    resolve: {
            newPath: function(){
                return 'home'
            },
            oldPath: function(){
                return 'home.modal'
            }
        },
    controller: 'ModalInstanceController'
  });
};
//... other code

Finally, the modal instance controller. This controller synchronizes the modal events (open/close) with URL path changes.

angular.module("app").controller('ModalInstanceController', function($scope,    $modalInstance, $state, newPath, oldPath) {
    $modalInstance.opened.then(function(){
        $state.go(newPath);
    });
    $modalInstance.result.then(null,function(){
        $state.go(oldPath);
    });
    $scope.$on('$stateChangeSuccess', function () {
        if($state.current.name != newPath){
            $modalInstance.dismiss('cancel')
        }
    });
});

Comments

3

You may create a state with the same templateUrl and controller as your page where you want to show the modal, adding params object to it

$stateProvider
    .state('root.start-page', {
        url: '/',
        templateUrl: 'App/src/pages/start-page/start-page.html',
        controller: 'StartPageCtrl'
    })
    .state('root.login', {
        url: '/login',
        templateUrl: 'App/src/pages/start-page/start-page.html',
        controller: 'StartPageCtrl',
        params: {
            openLoginModal: true
        }
    })

And in controller of the page, use this parameter to open the modal

.controller("StartPageCtrl", function($scope, $stateParams) {
    if ($stateParams.openLoginModal) {
        $scope.openLoginModal();
    }

Comments

2

I found a handy hint to get this working. There are probably caveats, but it works for me. You can pass a result still but I have no need for one.

Using finally instead of the then promise resolve sorted this for me. I also had to store the previous state on rootScope so we knew what to go back to.

Save previous state to $rootScope

$rootScope.previousState = 'home';
$rootScope.$on('$stateChangeSuccess', function(ev, to, toParams, from, fromParams){
    $rootScope.previousState = from.name;
})

State using onEnter

$stateProvider.state('contact', {
    url: '/contact',
    onEnter: function ($state, $modal, $rootScope){
        $modal.open({
            templateUrl: 'views/contact.html',
            controller: 'ContactCtrl'
        }).result.finally(function(){
            $state.go($rootScope.previousState);
        })
    }
});

Comments

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.