Creating Stateful Modals in AngularJS with Angular UI Router

Originally published at: http://www.sitepoint.com/creating-stateful-modals-angularjs-angular-ui-router/

There are a number of ways to approach implementing modals in an AngularJS application, including the popular angular-dialog-service and the official Angular-UI Bootstrap modal. In this article I’ll share how I like to handle modals in Angular, using another Angular-UI service, the ui-router.

Thinking in States

The core idea behind this approach is that modals are in fact unique states of your application. Consider an e-commerce site. When you click the “Add to cart” button, a modal pops up asking you to log in. You enter your details and click “Continue”, which show’s you another modal with the details of your cart.

You’ve just traversed a number of states that just happened to be in modals. If we think of modals as states, then it makes sense to use a state management tool to go to and from different modal states.

Getting Started with the UI Router

Let’s keep it simple to start with. First, we’ll install the ui-router and add it to our Angular app.

1. Create a Simple HTML Page

We’ll start by creating an index.html file with the following contents:

< !doctype html>
<html ng-app="modalApp">
  <head>
    <title>Modals with Angular and Ui-Router</title>
    <link rel="stylesheet" href="css/app.css" />
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/angular.min.js"></script>
    <script type="text/javascript" src="js/angular-ui-router.min.js"></script>
    <script type="text/javascript" src="js/app.js"></script>
  </head>
  <body>
  </body>
</html>

jQuery has been included for some DOM work later on.

In addition to including the latest version of Angular itself, I’ve included the Angular UI Router, a CSS file (currently empty), and of course our app’s main JavaScript file. Let’s take a look at that next.

2. Create Your Angular App

The app.js file is incredibly simple at this point. We just create a module for our modalApp and then add the ui.router as a dependency:

angular.module("modalApp", ["ui.router"])

3. Fleshing Out the Interface

Before we can open a modal, we need a UI for the user to interact with. In this instance I’ve kept things simple with an “Add to cart button” in index.html:

< !doctype html>
<html ng-app="modalApp">
  <head>
    <title>Modals with Angular and Ui-Router</title>
    <link rel="stylesheet" href="css/app.css" />
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/angular.min.js"></script>
    <script type="text/javascript" src="js/angular-ui-router.min.js"></script>
    <script type="text/javascript" src="js/app.js"></script>
  </head>
  <body>
    <h3>Pusheen Hoodie</h3>
    <p>Now you too can look like Pusheen!</p>
    <button>Add to cart</button>
  </body>
</html>

4. Configure the Initial States

We’re going to be defining a number of states for each of our modals, but there’s a bit of setup we need to do first. Since it’s likely that we’ll want to share behavior between our different modals, let’s create a parent Modal state that our individual modals can then inherit from. The following code belongs in js/app.js:

angular.module("modalApp", ["ui.router"]).config(function($stateProvider) {
  $stateProvider.state("Modal", {
    views:{
      "modal": {
        templateUrl: "modal.html"
      }
    },
    abstract: true
  });
});

Continue reading this article on SitePoint

Hi Brad,

Thanks! Clear explanation. Some questions/remarks:

  1. Why do you use jQuery to handle the click/keypress-events in the modal? I would love to see how this looks with an angular-only implementation.
  2. It took me a while to figure out how the two divs with ‘ui-view=“modal”’ relate to each other. I then renamed the second one to ‘modal-content’ instead of just ‘modal’, so the names don’t match. That’s easier to read, especially for beginners.

Thanks and best regards,

Rens

Hi Brad

This is really helpful.
Thanks for your work.

What happens if the current view is already a state? I mean if index.html had a ui-view that we used to load a state say Products.Listing and we wanted to add the add to cart button there, wouldn’t navigate to Modal.AddToCard state change remove us from Products.Listing state and thus not see the product listing as a background behind the backdrop?
And if Products.Listing was the only state that Add to cart modal was available it could be a child state, but if it has to be available from 10 other states as well?

I find states hard to use in modals, because a modal is not navigating you away from your view, rather just overlaying it, whereas a state change navigates you away from your previous state.

Thanks it was really very helpful and clear even though I am beginner!

What is stateful about this modal? Is the scope or controller of the modal re instantiated everytime you open it or not? Is the DOM of the modal added/remove to the ui-view or just hidden/shown? I am asking because I am looking for a modal that is truly stateful as it will have an iframe that cannot be reloaded when opened.

Thanks

I like it, the way you explained , thanks

Agree with mxa055 of troubles with use this use case of state-modal on alot of pages on your app. It is realy nice method but do not usefull for complex app-sites.

Hi all,

The point about the modal views conflicting with other app views is absolutely true. cc/ @i_am_brutto @mxa055

The context of this solution for me, was that my app was not driven by states - so I was able to use the states for modals without any conflicts.

I think the solution is still rather clean though - and would like to find a way to make it work. There’s potential for nesting the modals beneath the states they need to appear on which is ok for more specific modals but not generic site wide ones like a login modal per se.

The other option is to use something like Sticky States (http://christopherthielen.github.io/ui-router-extras/#/sticky) where you could maintain one route tree for regular states and another for modals.

I’d be happy to see any of you put forward a solution along those lines :slight_smile: PR’s are welcome on the articles repo: https://github.com/jsprodotcom/ui-router-modals

I like this approach and used it recently in a fairly complex app. I managed the ‘calling-state’ problem using the $previousState capability found in http://christopherthielen.github.io/ui-router-extras. This allows you to easily store the calling state of a modal and return to it on completion.

Do you have a working sample to try?

This was really helpful, thanks!

This example was very well writtern but unfortunatley it didnt work for me I get an Error

Error: Could not resolve ‘modal.termsandconditons’ from state ‘details’

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.