8

I am trying to open a modal dialog using Angular's ui-router as explained here.

The goal is for the dialog to be accessible anywhere, a url is not necessarily needed but it would be nice if I could link to a page with the dialog open.

Here is the broken sample:

http://plnkr.co/edit/BLkYME98e3ciK9PQjTh5?p=preview

clicking on "menu" should open the dialog from either page.

The routing logic:

app.config(function($stateProvider,$locationProvider, $urlRouterProvider, modalStateProvider) {
  $urlRouterProvider.otherwise("/");
  $locationProvider.html5Mode(true);

  $stateProvider
    .state("app", {
      url: "",
      abstarct: true,
      views: {
        "" : {
          templateUrl: "main.html",
        },
        "header@app": {
          templateUrl: "header.html"
        },
        "footer@app":{
          templateUrl: "footer.html"
        }
      }
    })

    .state("app.home", {
      url: "/",
      templateUrl: "home.html",
    })
    .state("app.content", {
      url: "/content",
      templateUrl: "content1.html",
    });


  modalStateProvider.state("app.home.menu", {
    template: "I am a Dialog!",
    controller: function ($scope) {
      $scope.dismiss = function () {
        $scope.$dismiss();
      };
    }
  });
});

It should not be a child of "app.home" since I want it to be accessible from anywhere. How can I achieve this?

Community
  • 1
  • 1
fusio
  • 3,595
  • 6
  • 33
  • 47

1 Answers1

17

You can do this with UI-Router Extras "Sticky States".

Updated plunk: http://plnkr.co/edit/GYMjzmBALlQNFWoldmxa?p=preview

Here is the UI-Router Extras modal demo: http://christopherthielen.github.io/ui-router-extras/example/stickymodal/#/


To update your plunk, I added UI-Router Extras:

  <script src="https://rawgit.com/christopherthielen/ui-router-extras/0.0.10/release/ct-ui-router-extras.js"></script>

var app = angular.module('plunker', ['ui.router', 'ct.ui.router.extras', 'ui.bootstrap']);

I added a named ui-view for the app and one for the modal

<body>
  <div ui-view="app"></div>
  <div ui-view="modal"></div>
</body>

Then I marked your app state as sticky and made your modal state a top-level state. The effect is that you can navigate from any app.* state to the modal state... instead of exiting that state, it will only "inactivate" it, and it remains in the DOM.

$stateProvider
.state("app", {
  url: "",
  abstract: true,
  sticky: true,

modalStateProvider.state("menu", {

updated with response to question in comments:

quick question: if I give the "menu" state a URL (/menu) and the user goes to that URL (website.com/menu) is there a way to set a "default" sticky for the app view? (sort of default parent of the modals)

You can do this yourself using a bunch of silly logic.

  • Is this the initial transition?
  • Are we going to the modal state?
  • Then cancel the transition and go to the default state instead.
  • When that's done, go to the modal state.

app.run(function($rootScope, $state) {
  $rootScope.$on("$stateChangeStart", function(evt, toState, toParams, fromState, fromParams) {
    if (fromState.name === "" && toState.name === "menu") {
      // fromState is the root state.  This is the initial transition.
      evt.preventDefault(); // cancel transition to modal.
      $state.go("defaultstate").then(function() { // Go to the default state
        $state.go(toState, toParams); // When that state has loaded, go back to the menu state.
      });
    }
  });
});
Chris T
  • 8,186
  • 2
  • 29
  • 39
  • quick question: if I give the "menu" state a URL (`/menu`) and the user goes to that URL (`website.com/menu`) is there a way to set a "default" sticky for the `app` view? (sort of default parent of the modals) @Chris – fusio Oct 08 '14 at 08:35
  • 1
    There is no mechanism currently to allow that. You could do some complicated conditionals yourself. I'll update the answer instead of typing it here. – Chris T Oct 08 '14 at 19:03
  • @ChrisT If a sticky state has many many child states, and all of those child states have controllers, then by keeping all of the states alive if inactive, could there be any potential memory allocation issues? – claireablani Jul 09 '15 at 23:29
  • Yes. You should definitely consider that – Chris T Jul 10 '15 at 00:32
  • @ChrisT I see. That's too bad. It would otherwise be such an elegant solution for displaying nomadic modals and preserving user activity in various states. I counted watchers within each child state of the sticky state. Inactive states' controller bindings were not counted. So it would be rather that the controllers are taking up space merely by existing and defined? – claireablani Jul 10 '15 at 06:50
  • @ChrisT Also when restoring a child state from inactive to active, the controller is destroyed and reinstantiated immediately afterwards at that point? It's not that they had already been destroyed upon transitioning to the inactive state in the first place? – claireablani Jul 10 '15 at 06:52
  • Going from active to inactive and back, the controller is not destroyed or reinstantiated. Only when the state is entered or exited. Sticky states is so multiple states can be running fully, in parallel. (I almost called it parallel states) – Chris T Jul 10 '15 at 15:07
  • @ChrisT I've switched to the exact same structure for my html and config, but one of my states using the `app` view contains a resolve which the controller uses. When I enter the modal state, the `resolve` variable becomes throws an unknown provider error. Is this suppose to happen in sticky states? – dmr07 Oct 26 '15 at 17:44
  • Yes, any normal state gets resolves from itself and its parent states. Sticky states work the same way. – Chris T Oct 28 '15 at 22:10
  • How to pass param to modal in your example? I tried with $stateparm and it is always empty. – indusBull Oct 12 '17 at 21:18