Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a popup like state using AngularJS ui-router

I'm trying to create a state which acts like a popup, i.e it doesn't clear the current state, it just pops over it without completely destroying the current state (So that the user can gain access to it by dismissing the popup).

Heavily simplified, my applications routes is something like the following:

angular.module('test', ['ui.router'])
  .config(['$stateProvider', '$urlRouterProvider',
    function($stateProvider, $urlRouterProvider) {
      $stateProvider
        .state('login', {
          url: '/login',
          template: '<button><a ui-sref="authenticated.home">Login</a></button>'
        })
        .state('authenticated', {
          url: '/authenticated',
          template: '<p>We are now authenticated</p>' +
            '<a ui-sref="authenticated.home">home</a>' +
            '<a ui-sref="authenticated.statistics">statistics</a>' +
            '<a ui-sref="authenticated.popup">Popup!</a>' +
            '<div ui-view></div>' +
            '<div ui-view="popup"></div>'
        })
        .state('authenticated.home', {
          url: '^/home',
          template: '<p>We are in home. <br><input></p>'
        })
        .state('authenticated.statistics', {
          url: '^/statistics',
          template: '<p>We are in statistics. <br><input></p>'
        })
        .state('authenticated.popup', {
          url: '^/popup',
          views: {
            popup: {
              template: '<div id="popup"><p>popup is up</p>' +
                '<a ui-sref="authenticated.home">close</a>' +
                '</div>'
            }
          }
        });
      $urlRouterProvider.otherwise('/login');
    }
  ]);
a {
  margin-right: 20px;
  text-decoration: none;
}
#popup {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
  background: #000;
  color: #fff;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="test">
  <div ui-view>
  </div>
</div>
  • User is presented with a login screen
  • Once logged in, user is redirected to authenticated.home state. The authenticated parent state holds a navigation menu and <ui-view> to attach subviews
  • User can use this navigation to navigate around the application to other routes like authenticated.statistics, authenticated.popup.

The problem is that, when I simply move to the popup state, even though I have specified popup view inside it's views object, it clears the other ui-view (makes sense though, because we're no longer in a state that matches it).

One solution I can think of is to use something like ui-router-extras to go back to previous state, the problem with this is that any changes the user might have been making in the previous states will be lost.

Another solution will be to have the template of popup in the authenticated states template and show/hide it. But the problem with this is that, the popup should be a bookmark-able state, which loads data from server based on state params.

Is there a better approach to create a state that acts like a popup over current state? maybe by changing the template structure or using something like abstract-states that I haven't thought of?

like image 893
T J Avatar asked Apr 19 '16 07:04

T J


2 Answers

The sticky states add on from ui-router-extras should be what you're looking for. Gives you the ability to create sticky/parallel states so it should allow you to create a popup state without affecting the original state you were in.

I haven't experimented enough with it to know all the details but the main idea is to move all you main states under a root app state and set sticky to true:

$stateProvider.state('app', {
      url: '',
      views: {
        'app': {
          templateUrl: 'app.html',
          controller: 'mainCtrl',
        }
      },
      sticky: true
  });

  $stateProvider.state('app.account', {
      url: '/account',
      templateUrl: 'account.html'
  });

  $stateProvider.state('app.account.stuff', {
      url: '/stuff',
      template: "<h3>Here's my stuff:</h3><ul><li>stuff 1</li><li>stuff 2</li><li>stuff 3</li></ul>"
  });

After that add your modal state as a sibling (so not as a child of the root app state)

$stateProvider.state('modal', {
      url: '/modal',
      views: {
        'modal': {
          templateUrl: 'modal.html'
        }
      }
  });

I took the example provided in the docs and made a few edits to add controllers and simplify it: http://plnkr.co/edit/4JGdIcDUQs0Za4fBaLj1?p=preview

like image 172
Ahmed Wagdi Avatar answered Sep 22 '22 01:09

Ahmed Wagdi


sounds like something abstract states could fix.

changing your "authenticated" state to an abstract parent and attaching a controller to it can allow you to axx data being transformed by child states. Also you could just make a separate controller for your data and drop an ng-controller on the body tag.

If you're still having probs implementing, how about you use LocalStorage to persist the data you want?

Update2 Found a post and changed the plnk on updating ng-model as you type, but i see why OP wants a popup.

Edit Plnkr Shared State Example

I'll try to find a good example, but here's some pseudo code for now.

.state('authenticated', {
      abstract: true,
      url: '/authenticated',
      controller: 'AuthStateController',
      template: '<p>We are now authenticated</p>' +
        '<a ui-sref="authenticated.home">home</a>' +
        '<a ui-sref="authenticated.statistics">statistics</a>' +
        '<a ui-sref="authenticated.popup">Popup!</a>' +
        '<div ui-view></div>' +
        '<div ui-view="popup"></div>'
    })

I believe with or without {abstract: true} your popup state should have access to data inside of 'AuthStateController'. The resolve stuff may be an overkill solution, but might give you an idea of how to use the controllers data more efficiently.. if all else fails .. build a service to handle the data.

.state('authenticated.popup', {
      url: '^/popup',
      views: {
        popup: {
          resolve: {
            ctrl: 'AuthStateController',
            data: function(ctrl) {
              return ctrl.data_for_popup;
            }
          }
          template: '<div id="popup"><p>popup is up</p>' +
            '<a ui-sref="authenticated.home">close</a>' +
            '</div>'
        }
      }
    })
like image 29
4UmNinja Avatar answered Sep 22 '22 01:09

4UmNinja