AngularJS: How to set one state as a global parent

2019-01-28 14:43发布

In $stateProvider#state(), I define it as follows for Bootrtrap-ui modal using UI-Router. reference

var state = {
    name: 'modala',
    parent: 'home',
    onEnter: function($modal, $state) {
        modalInstance = $modal.open({
            templateUrl: 'modala.html',
            controller: 'modalaCtrl',
            controllerAs: 'modal'
        });
        modalInstance.result['finaly'](function() {
            modalInstance = null;
            if ($state.$current.name === 'modala') {
                $state.go('^');
            }
        });
    },
    onExit: function() {
        if (modalInstance) {
            modalInstance.close();
        }
    }
};

I want to use 'modala' any place other than home.

I do not want to create a lot of definitions of 'modala'.

Is there a method to accept it, and to set what is necessary in parent?


add explanation

No good solutions

  1. Don't set parent in modal state.

    result: when open modal window, parent isn't displayed.

    pattern 1 example

  2. modal state name is ${parent.name}.modal format.

    result: It's work. but, About one modal window, it is necessary to define many states. and, It is necessary to add a state whenever I add an parent who call modal.

    pattern 2 exapmle

  3. define the modal state every parent

    result:same as pattern 2.

    pattern 3 exapmle

4条回答
贼婆χ
2楼-- · 2019-01-28 15:20

All you have to do is add the property parent: modala to all child states that you define in order to set a parent state.

查看更多
Summer. ? 凉城
3楼-- · 2019-01-28 15:30

I was wanting the solution to this too. I eventually worked it out.

See my stackoverflow answer / question

查看更多
疯言疯语
4楼-- · 2019-01-28 15:31

There is a solution built inside of the UI-Router, called state decorator

decorator(name, func)

This way, we can create an aspect, which will be injected into any state we want later. That aspect, could drive some common, global setting. E.g. parent or views...

Check an example here: How to decorate current state resolve function in UI-Router? Current function isn't invoked, small cite:

Allows you to extend (carefully) or override (at your own peril) the stateBuilder object used internally by $stateProvider. This can be used to add custom functionality to ui-router, for example inferring templateUrl based on the state name...

There is a working example for our scenario

What we would need, is the decorator definition, like this:

.config(['$stateProvider', 
  function($stateProvider) {

    $stateProvider.decorator('parent', function (state, parent) {
      // this is original, by configuration created parent
      var result = parent(state);

      // we can define where we do not want to change that default, configured
      var skipState = state.name === "home" 
                   || state.name === "modala"
                      // child already has that in parent
                   || state.name.indexOf(".") > 0 ;

      // and return taht default
      if(skipState) 
      { 
        return result;
      }

      // FINALLY - HERE
      //   we want to change parent, or even introduce new
      state.parent = 'modala'; 

      // and rebuild it
      result = parent(state);

      // new parent in place
      return result;

    });
}])

Check that in action here

查看更多
Juvenile、少年°
5楼-- · 2019-01-28 15:38

There is updated plunker (the second try from the question)

What we would see in action, is different use of decorator, as in detail described in my previous post

So, firstly, let's have few states e.g. 'home', 'page1', 'page2' ... and we would like for all of them introduce the child state - with modal functionality. That modal features are expected to be the same across all child states - but these will belong to different parents.

To make it happen we can use state definition like this:

.config(function($stateProvider) {
  // we just define empty child state for each existing parent
  $stateProvider.state('home.generalModal',  {});
  $stateProvider.state('page1.generalModal', {});
  $stateProvider.state('page2.generalModal', {});
})

And that could be enough, if we will hijack the decorator this way:

.config(['$stateProvider', 
  function($stateProvider) {

    var endsWith = function(stringToBesearched, valueToBeFound) {
      return stringToBesearched.slice(-valueToBeFound.length) === valueToBeFound;
    }

    $stateProvider.decorator('data', function (state, dataBuilder) {
      // this is original, by configuration created parent
      var data = dataBuilder(state);

      // we can define where we do not want to change that default, configured
      // in our case, we extend every state which ends with .generalModal
      var skipState = !endsWith(state.name, ".generalModal")

      // and return taht default
      if(skipState) 
      { 
        return data;
      }

      // the modal instance per this state instance
      var modalInstance;

      // definition of the onEnter
      var onEnter = function($modal, $state) {
          console.log("Entering state: " + $state.current.name)
          modalInstance = $modal.open({
            templateUrl:'modal.html',
            controller: 'modalCtrl',
            controllerAs: 'modalCtrl'
          });
          modalInstance.result['finally'](function() {
            modalInstance = null;
            if(endsWith($state.$current.name, ".generalModal")) {
              $state.go('^');
            }
          });
        };

      // definition of the onExit
      var onExit = function($state) { 
          console.log("Exiting state: " + $state.current.name)
          if (modalInstance) {
            modalInstance.close();
          }
      };

      // extend state with both of them
      state.self.onEnter = onEnter;
      state.self.onExit = onExit;

      return data;

    });
}])

We extended every child state which name endsWith ".generalModal" with onExit and onEnter. That's all. At one place...

Check it in action here

查看更多
登录 后发表回答