July 03, 2015 Marc Perrin-Pelletier 3 min read

Stateful modals with Angular UI router

Modals are very useful to capture user focus, thus enhancing user experience.

Their use was largely popularized by Twitter Bootstrap and now by its Angular-equivalent: Angular UI Bootstrap.

This article assumes you are familiar with the Angular UI router and Angular. Code samples are written in Coffeescript and Jade.

Creating a modal with UI Bootstrap

First you need to set up the action trigger. In our case, we'll use a simple button .

# home/states/main/view.jade (View from which the modal is launched) //... button.btn.btn-default(ng-click="launchModal()") //...

launchModal is called by the ng-click directive and triggers the modal. If you need to pass arguments to your modal, you can add a resolve attribute, as shown below.

angular . module 'home-module' . controller 'HomeController' , ( $scope ) - > $scope . foo = 'bar' $scope . launchModal = - > modalInstance = $modal . open animation : true templateUrl : 'home/modals/mymodal/view.html' size : 'lg' controller : 'MyModalCtrl' resolve : myVar : - > $scope . foo modalInstance . result . then ( anotherVar ) - > $scope . anotherVar = anotherVar

Notice myVar is then available in the Modal controller by adding it as a dependency.

angular . module 'home-module' . controller 'MyModalCtrl' , ( $scope , $modalInstance , myVar ) - > $scope . myVar = myVar $scope . ok = - > / / ... $modalInstance . close $scope . anotherVar $scope . cancel = - > $modalInstance . dismiss 'cancel'

The Modal view :

# home/modals/mymodal/view.jade (Modal view) .modal-header h3.modal-title span This is the modal title. span.pull-right i.fa.fa-remove.cursor(ng-click="cancel()") .modal-body p This is the modal body. `myVar` is available here : {{ myVar }} .modal-footer button.btn.btn-primary(ng-click="ok()") button.btn.btn-link(ng-click="cancel()")

If need be, you may return a variable on modal closure, in our case anotherVar . This variable is passed down to the modal promise.

angular . module 'home-module' . controller 'HomeController' , ( $scope ) - > $scope . foo = 'bar' $scope . launchModal = - > modalInstance = $modal . open animation : true ... modalInstance . result . then ( anotherVar ) - > console . log 'Promise has resolved' $scope . anotherVar = anotherVar , - > console . log 'Promise was rejected'

Making it stateful

A great way to improve the ergonomy of your application is to make some modals stateful: if your modal represents a key step in your application - login, subscribe, view my cart, etc-, as opposed to an alert or confirmation modal, then it should have its own url.

This is made possible by Angular UI Router, by linking your modal to a state with onEnter :

angular . module 'home' , [ ... ] . config ( $stateProvider ) - > $stateProvider . state 'home' , url '/home' ... . state 'home.properties' , url : '/properties/:foo' onEnter : ( $modal , $state , $stateParams ) - > modalInstance = $modal . open animation : false templateUrl : 'home/modals/mymodal/view.html' controller : 'MyModalCtrl' size : 'lg' resolve : myVar : - > $stateParams . foo modalInstance . result . finally - > $state . go '^'

The state home.properties is a child state of home . It will load its template in its parent's ui-view , as demonstrated below. Moreover the modal is triggered by a ui-sref attribute, as you would do with a link. Finally, $state.go '^' redirects you to the parent state when the modal promise is resolved.

# home/states/main/view.jade (View from which the modal is launched) //... button.btn.btn-default(ui-sref="home.properties({foo: 'bar'})") //... .ui-view

Conclusion

That's all folks! If you want to see a live example of stateful modals, you can check out Trello.