Stateful modals with Angular UI router
Marc Perrin-Pelletier3 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.
# home/states/main/controller.coffee (Controller of the view from which the modal is launched)
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.
# home/modals/mymodal/controller.coffee (Modal controller)
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.
# home/states/main/controller.coffee (Controller of the view from which the modal is launched)
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
:
# home/module.coffee
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.