opening a modal in a route in AngularJS with angular-ui-bootstrap

31,141

Solution 1

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.

Solution 2

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

Solution 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();
    }

Solution 4

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);
        })
    }
});
Share:
31,141
Darren Wainwright
Author by

Darren Wainwright

Updated on August 15, 2022

Comments

  • Darren Wainwright
    Darren Wainwright over 1 year

    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.

  • Viktor Eriksson
    Viktor Eriksson almost 7 years
    This does not seem to close the dialog when pressing back though