5

I want to temporarily change the browser url when the ui bootstrap modal is opened ( The page behind should remain as is, only the url changes ). When the modal is closed the url should be reverted back to the original one.

Steps :

  1. User loads the page
    • url : xyz.com/home
  2. User clicks a link opens a modal
    • url : xyz.com/detail/123
    • possible solution : changing url with html5 push state
    • problem : Angular ui-router tries to run its routes as per the changed url, eventually changing the background page.
  3. User closes the modal
    • url : xyz.com/home
    • possible solution : html5 pop state
    • problem : Reloads the background page, which kills the purpose

Example implementation : Pinterest pins and their pin details popup.

8
  • 1
    You need to create your modal as a state in order to achieve this. Commented Jun 6, 2016 at 16:42
  • My modal is opened from an isolate scope directive, basically can be invoked from any state in the application. Does the modal state need the parent state to be abstract ? Have you worked with angular ui-router-extras sticky modal ? Commented Jun 6, 2016 at 16:47
  • 4
    plnkr.co/edit/qgt0RkEnXDPDTdMJPIfy?p=preview Commented Jun 6, 2016 at 17:11
  • Will you please post a snippet of your code? Commented Jun 7, 2016 at 19:54
  • Add a state with the same parameters except its name Commented Jun 9, 2016 at 12:30

4 Answers 4

1

You can use ui-router-extras sticky state to solve your problem. There is simple example with modal by the link. You should create two named views, one for main content (background) and one for modal.

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

Mark the state, from what you want to access to modal as sticky: true in route definition.

.state('main', {
  abstract: true,
  url: '/',
  templateUrl: '_layout.html'
})
.state('main.index', {
  url: '',
  sticky: true,
  views: {
    'app': {
      templateUrl: 'index.html'
    }
  }
})
.state('main.login', {
  url: 'login/',
  views: {
    'modal': {
      templateUrl: 'login.html'
    }
  }
})

Also add an event for stateChangeSuccess:

$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
  if ((from.views && !from.views.modal) || !from.views) {
    $rootScope.from = from;
    $rootScope.fromParams = fromParams;
  }
});

so, when you need to close modal, you can just

$state.go($rootScope.from, $rootScope.fromParams);

There is small problem for that solution. If you reload page on the modal state, then the app ui-view will be empty.

Sign up to request clarification or add additional context in comments.

Comments

0

This can be achieved by having a nested state and triggering the modal using onEnter callback:

$stateProvider
  .state('contacts', {
    url: '/home',
    templateUrl: 'home.html',
    controller: function($scope, MyService){
      $scope.contacts = MyService.getContacts();
    }
  })
  .state('contacts.details', {
    url: "^/details/:id", // using the absolute url to not have the "/home" prepended
    onEnter: function($state, $uibModal) {
      var modal = $uibModal.open({
        templateUrl: 'details.html',
        controller: function($scope, $stateParams, MyService) {
            // get data from service by url parameter
            $scope.contact = MyService.getContact($stateParams.id);
        }
      });

      modal.result.finally(function() {
        $state.go('^'); // activate the parent state when modal is closed or dismissed
      });
    }
  });

This technique is described in the ui-router's FAQ.

Here the plunk. In this example the modal's scope is created as a child of the $rootScope - the default $uibModal's behavior when no scope is passed to it. In this case we should use the service in the modal's controller to obtain the data by url parameter.

To have master and details URLs look like these - xyz.com/home and xyz.com/detail/123 - we should use the absolute URL (^/details/:id) in the child state.

Using this solution you can open the detail URLs directly and still have both, master and detail states, activated properly, so sharing the detail URL is possible.

2 Comments

In this solution You need to create a modal route state for each state You want the modal to be opened. And the url can't be like this: url : xyz.com/home for page and url : xyz.com/detail/123 for modal
@E.Abrakov, yes, modal opening and closing logic should be written explicitly for every state. As for the URLs - you can use the absolute URL in the child state - see the updated answer and plunk.
0

I think you can achive that with ngSilent module

https://github.com/garakh/ngSilent

using $ngSilentLocation.silent('/new/path/'); (once you open modal and again after closing it)

Comments

0

Managed to implement this using https://github.com/christopherthielen/ui-router-extras/tree/gh-pages/example/stickymodal

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.