遍历多视图状态树时,UI路由器过渡已被取代 [英] UI Router transition superseded when traversing multi-views state-tree

查看:48
本文介绍了遍历多视图状态树时,UI路由器过渡已被取代的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过具有多个视图的波纹管状态来管理我的管理页面:admin,admin.header,admin.leftPanel,admin.main,admin.tail.在头文件leftPanel,main和tail中,我分别使用$ state.go为其子状态呈现其内容.我编写了下面的简单代码来演示此问题.

I'm trying to manage my admin page by bellow states with multi views: admin, admin.header, admin.leftPanel, admin.main, admin.tail. In the header, leftPanel, main and tail, I use $state.go to their sub states respectively to render their contents. I write bellow simple code to demo this problem.

演示状态模型:

state1:
  state2view
    controller: $state.go(state1.state2) <---superseded
  state3view
    controller: $state.go(state1.state3)

代码( plunker ):

<!DOCTYPE html>
<html ng-app="demo">

  <head>
    <meta charset="utf-8" />
    <title>Demo</title>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.6/angular.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/1.0.3/angular-ui-router.js"></script>
    <script>
      let app = angular.module('demo', ['ui.router']);
    
      app.config(['$urlRouterProvider', '$stateProvider', function ($up, $sp) {
        $sp.state('state1', state1);
        $sp.state('state1.state2', new SubState('state2view'));
        $sp.state('state1.state3', new SubState('state3view'));
        $up.otherwise('/');
      }]);
      
      let state1 = {
        url: '/',
        views: {
          "state1view1": {
            controller: ['$transition$', '$state', function ($tr, $st) {
              this.stateName = $st.current.name;
              $st.go('state1.state2', {message: 'message from ' + $st.current.name + ' to ' + $st.current.name + '.state2'});
            }],
            controllerAs: '$ctrl',
            template: `<div>
              {{$ctrl.stateName}} begin<br>
              <ui-view name="state2view"></ui-view>
              {{$ctrl.stateName}} end
            </div>`
          },
          
          "state1view2": {
            controller: ['$transition$', '$state', function ($tr, $st) {
              this.stateName = $st.current.name;
              $st.go('state1.state3', {message: 'message from ' + $st.current.name + ' to ' + $st.current.name + '.state3'});
            }],
            controllerAs: '$ctrl',
            template: `<div>
              {{$ctrl.stateName}} begin<br>
              <ui-view name="state3view"></ui-view>
              {{$ctrl.stateName}} end
            </div>`
          }
        }
      };
      
      function SubState(view1Name) {
        this.params = {message: ''};
        this.views = {};
        this.views[view1Name] = {
          controller: ['$transition$', '$state', function ($tr, $st) {
            this.parentMessage = $tr.params().message;
            this.stateName = $st.current.name;
          }],
          controllerAs: '$ctrl',
          template: `<div>
            {{$ctrl.stateName}} begin<br>
            {{$ctrl.parentMessage}}<br>
            {{$ctrl.stateName}} end
          </div>`
        };
      }
      
      app.run(function($transitions) {
        $transitions.onStart({}, function($tr) {
            console.log("trans begin: " + $tr.from().name + " -> " + $tr.to().name);
          }
        );
        $transitions.onSuccess({}, function($tr) {
            console.log("trans done: " + $tr.from().name + " -> " + $tr.to().name);
          }
        );
      });

    </script>
    <style>
    div{border-style: solid;}
    </style>
  </head>

  <body>
    <ui-view name="state1view1"></ui-view>
    <br>
    <ui-view name="state1view2"></ui-view>
  </body>

</html>

预期结果:

state1 begin
state1.state2 begin
message from state1 to state1.state2
state1.state2 end
state1.state3 begin
message from state1 to state1.state3
state1.state3 end
state1 end

实际结果:

state1 begin
state1 end
state1 begin
state1.state3 begin
message from state1 to state1.state3
state1.state3 end
state1 end

控制台输出:

推荐答案

事实证明,我只是碰壁.

It turns out that I just hit the wall.

通过一个程序一个页面地访问页面内容来将页面内容包含在UI Router状态和布局中的想法是完全错误的..尤其是,您不能显示两个同级对象一次表达国家的观点.

The idea of enclosing a page's contents into UI Router states and layout by visiting these states one by one programmablly is totally wrong. Particularly, you can not show two sibling states' views in one time.

UI路由器专为通过单击鼠标进行路由而设计.即使有序的文档强烈暗示我们可以在状态树上中继整个页面以布局所有内容,但情况并非总是如此.只要应用逻辑转移到不是起始状态的后继状态的另一状态,则起始状态会在进入到状态之前退出,并且其运行时生成的退出状态视图将被完全删除

UI Router is designed for routing by mouse clicks. Even though the pieced documents strongly hint we can relay our full page on a state tree to layout all contents, but it is not always the case. As long as the app logic transit to another state which is not the decedent of the from-state, the from-state exits before entering the to-state, and its run-time generated view(s) of the exited state is fully removed.

下面的代码最初是在尝试证明我的概念(由于我意识到状态中没有断点/恢复设计,所以对原始问题的设计有所改进)并解决了我的问题,但是事实证明是揭示相反的一个例子-错误观念下的不可能.

Bellow code is originally trying to prove my concept bellow (the improvement of the design in the original question, since I realized that there is not break-point/resume design in state) and to solve my problem, but it turns out to be a example of revealing the opposite -- the impossibility under the wrong idea.

概念

  1. 定义状态及其层次结构和模板以构架页面布局;
  2. 制作状态树;
  3. 进行遍历;
  4. 通过$ state.go遍历到每个州的路径以展开并呈现布局.

代码( plnkr )

<!DOCTYPE html>
<html ng-app="demo">

  <head>
    <meta charset="utf-8" />
    <title>Demo</title>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.6/angular.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/1.0.3/angular-ui-router.js"></script>

    <script>
      let app = angular.module('demo', ['ui.router']);

      app.config(['$urlRouterProvider', function ($up) {
        $up.otherwise('/');
      }]);

      app.provider('runtimeStates', ['$stateProvider', function ($stateProvider) {
        this.$get = function () {
          return {
            newState: function (name, param) {
              $stateProvider.state(name, param);
              return name;
            }
          };
        };
      }]);

      app.factory('sharingSpace', function () {
        return {
          stateTree: [],
          traversePath: []
        };
      });

      app.run(['sharingSpace', '$transitions', '$state' ,function(ss, $trs, $st) {
        $trs.onStart({}, function($tr) {
            console.log("trans begin: " + $tr.from().name + " -> " + $tr.to().name);
          }
        );
        $trs.onSuccess({}, function($tr) {
            nextHop(ss, $st);
            console.log("trans succeeded: " + $tr.from().name + " -> " + $tr.to().name);
          }
        );
      }]);

      app.run(['runtimeStates', 'sharingSpace', function(rt, ss) {
        makeStateTree(rt, ss);
      }]);

      function StateParam(stateName) {
        let me = this;
        me.name = stateName;
        me.params = {
          message : {
            value: '',
            dynamic: true
          }
        };
        me.views = {};
        //me.sticky = true; <---does not prevent the view port from removed when exit.
        me.onExit = ['$state', function($state){
          let goodByeMsg = 'Goodbye ' + $state.current.name;
          console.log(goodByeMsg);
          alert(goodByeMsg);
        }];
        me.addView = function(viewParam) {
          me.views[viewParam.name] = {
            controller: viewParam.controller,
            controllerAs: viewParam.controllerAs,
            template: viewParam.template,
          };
          return me;
        };
        return me;
      }

      function makeStateTree(rt, ss) {

        let state1view1param = {
          name: 'state1view1',
          controller: ['sharingSpace', '$transition$', '$state', function (ss, $tr, $st) {
            this.stateName = $st.current.name;
            this.viewName = 'state1view1';
            makeTraversePath(ss);
            //do something ...
          }],
          controllerAs: '$ctrl',
          template: `<div>
            {{$ctrl.stateName}}.{{$ctrl.viewName}} begin<br>
            let's start ...<br>
            <ui-view name="state2view"></ui-view>
            {{$ctrl.stateName}}.{{$ctrl.viewName}} end
          </div>`
        }

        let trivialCtrl = function(viewName) {
          return ['sharingSpace', '$transition$', '$state', function (ss, $tr, $st) {
            this.parentMessage = $tr.params().message;
            this.stateName = $st.current.name;
            this.viewName = viewName;
            //do something ...
            console.log('this.stateName = ' + this.stateName);
          }];
        };

        let state1view2param = {
          name: 'state1view2',
          controller: trivialCtrl('state1view2'),
          controllerAs: '$ctrl',
          template: `<div>
            {{$ctrl.stateName}}.{{$ctrl.viewName}} begin<br>
            <ui-view name="state3view"></ui-view>
            {{$ctrl.stateName}}.{{$ctrl.viewName}} end
          </div>`
        }

        let state2viewParam = {
          name: 'state2view',
          controller: trivialCtrl('state2view'),
          controllerAs: '$ctrl',
          template: `<div>
            {{$ctrl.stateName}}.{{$ctrl.viewName}} begin<br>
            parentMessage: {{$ctrl.parentMessage}}<br>
            {{$ctrl.stateName}}.{{$ctrl.viewName}} end
          </div>`
        }

        let state3viewParam = {
          name: 'state3view',
          controller: trivialCtrl('state3view'),
          controllerAs: '$ctrl',
          template: `<div>
            {{$ctrl.stateName}}.{{$ctrl.viewName}} begin<br>
            parentMessage: {{$ctrl.parentMessage}}<br>
            {{$ctrl.stateName}}.{{$ctrl.viewName}} end
          </div>`
        }
        let mainStateParam = new StateParam('state1');
        mainStateParam.url = "/";
        mainStateParam.addView(state1view1param).addView(state1view2param);
        let subStateParam1 = (new StateParam('state1.state2')).addView(state2viewParam);
        let subStateParam2 = (new StateParam('state1.state3')).addView(state3viewParam);
        
        rt.newState(mainStateParam.name, mainStateParam);
        ss.stateTree.push(rt.newState(subStateParam1.name, subStateParam1));
        ss.stateTree.push(rt.newState(subStateParam2.name, subStateParam2));

      }

      function makeTraversePath(ss) {
        for(let i = 0; i<ss.stateTree.length; i++){
          ss.traversePath.push(ss.stateTree[i]); //trivial example
        };
      }

      function nextHop(ss, $st){
        if(ss.traversePath[0] != undefined) {
          let nextHop = ss.traversePath[0];
          ss.traversePath.splice(0, 1);
          console.log('nextHop = ' + nextHop);
          $st.go(nextHop, {message: 'message from ' + $st.current.name});
        }
      }

    </script>

    <style>
      div{border-style: solid;}
    </style>
  </head>

  <body>
    <ui-view name="state1view1"></ui-view>
    <br>
    <ui-view name="state1view2"></ui-view>
  </body>

</html>

结果(Firefox 57.0.1)

The result (Firefox 57.0.1)

进入页面时:

单击并关闭警报后:

After click and close the alert:

如上图所示,上述过程显示state1.state2已执行并布局(但尚未通过angular进行评估/渲染).那时尚未退出,因为onExit警报弹出窗口阻止了该过程.警报弹出窗口关闭后,状态退出,视图已完全删除.

Above process revealed that the state1.state2 was executed and layed out (but not evaluated/rendered by angular yet), as we can see in the first picture. At that point the exiting was not happened yet because the onExit alert pop blocked the process. After the alert pop closed the state exited and the view was complete removed.

有专门针对页内标签开发的粘性状态目的,但正如我尝试过的那样,在这里不起作用.它会记住上次访问的状态,但始终会删除已存在状态的视图.

There is a sticky-state developed for the in-page-tab specific purpose, but as I tried it does not work here. It remembers the last visited stick-states but the views of the exited states are always removed.

我现在正在尝试仅将UI路由器用作路由表示法工具.但是我必须非常有意识地不要误以为 UI路由器可以用作布局页面(如角度组件的扩展)的通用工具.但这可能很困难:我现在无法想到使用UI路由器的正确模式.在多视图的情况下,如果任何两个兄弟视图都具有自己的子状态,则我必须非常小心,因为访问一个会退出另一个状态-它们是排他的.这使我认为这不值得其复杂性.

I'm now trying to use the UI Router as a routing notation facility only. But I have to be very conscious NOT to run into the idea that UI Router can be used as a general tool to layout a page like a extension of angular component. But this can be difficult: I cannot think of a right pattern to use UI Router at the moment. In case of multi views, if any two sibling views both has their own sub states, I must be very careful because visiting one exits another -- they are exclusive. This makes me think it is not worth its complexity.

尽管在导航过程中大多数情况下都希望在退出时删除视图,但我建议UI路由器进行更改,并有机会保留视图以提供更大的灵活性.它可能比最初的想法要复杂,但应该可以.

While removing views on exit is desired in most case during navigation, I would suggest the UI Router to change and give a chance to keep the views to provide more flexibility. It can be more complicated than the first thought, but it should be possible.

还希望为每个状态(不仅仅是粘性状态)缓存所有最后看到的"参数,以便我们可以轻松地返回它们.您可能会争论用例,但是我们无法想象人们将如何使用工具,并且不应该限制可能性.

It is also desirable to cache all the "last seen" parameters for each states (not just for sticky states) so we can return to them easily. You may argue the use case, but we cannot imagine how people will use a tool and should not limit the possibilities.

还希望为每个状态库提供完整的生命周期挂钩功能(现在仅具有onEnter和onExit).

It is also desirable to provide facility for full life cycle hooks per state base (now only have onEnter and onExit).

这篇关于遍历多视图状态树时,UI路由器过渡已被取代的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆