ui-router 动态创建嵌套状态 [英] ui-router creating nested states dynamically

查看:27
本文介绍了ui-router 动态创建嵌套状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的网页创建一个垂直导航面板(一项非常基本的任务).考虑到不同的用户角色对服务器上的授权模块应该有不同的导航项,因此希望通过从服务器获取数据来动态而不是静态地创建导航内容.

我正在尝试使用 UI 路由器动态创建嵌套状态(这确实是一个很自然的想法,称为分而治之")但遇到了问题(我在 另一个线程 但只有代码片段,无法演示).我在这里以更通用的方式为这个问题构建了一个简单的演示.

<html ng-app="演示"><头><meta charset="utf-8"/><title>演示</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"></脚本><脚本>让 app = angular.module('demo', ['ui.router']);app.provider('runtimeStates', ['$stateProvider', function ($stateProvider) {this.$get = 函数 () {返回 {新状态:函数(名称,参数){$stateProvider.state(name, param);返回名称;}};};}]);app.config(['$urlRouterProvider', '$stateProvider', function (up, sp) {sp.state('state1', state1);up.otherwise('/state1');}]);让 state1 = {网址:'/state1',控制器:['runtimeStates','$state',函数($rs,$st){this.stateName = $st.current.name;this.createSubState = function(){$st.go($rs.newState($st.current.name + '.state2', state2), {消息:'来自'+$st.current.name+'的消息到state2'});}}],controllerAs: '$ctrl',模板:`<div ng-click="$ctrl.createSubState()" style="border-style: solid; cursor: pointer;"><p>{{$ctrl.stateName}} 开始</p><ui-view></ui-view><p>{{$ctrl.stateName}} end</p>

`};让 state2 = {参数:{消息:''},控制器:['runtimeStates','$transition$','$state',函数($rs,$tr,$st){this.parentMessage = $tr.params().message;this.stateName = $st.current.name;this.createSubState = function(){$st.go($rs.newState($st.current.name + '.state3', state3),{消息:'来自'+$st.current.name+'的消息到state3'});};}],controllerAs: '$ctrl',模板:`<div ng-click="$ctrl.createSubState()" style="border-style: solid; cursor: pointer;"><p>{{$ctrl.stateName}} 开始</p>{{$ctrl.parentMessage}}<ui-view></ui-view><p>{{$ctrl.stateName}} end</p>

`};让 state3 = {参数:{消息:''},控制器:['runtimeStates','$transition$','$state',函数($rs,$tr,$st){this.parentMessage = $tr.params().message;this.stateName = $st.current.name;}],controllerAs: '$ctrl',模板:`<div style="border-style:solid;"><p>{{$ctrl.stateName}} 开始</p>{{$ctrl.parentMessage}}<p>{{$ctrl.stateName}} end</p>

`};<身体><ui-view></ui-view></html>

当state1的view被填充时,我可以点击它并生成带有预期内容的state2的view;但是当继续点击state2的view时,生成的内容完全乱七八糟.预期:

state1 开始state1.state2 开始从状态 1 到状态 2 的消息state1.state2.state3 开始从 state1.state2 到 state3 的消息state1.state2.state3 结束state1.state2 结束状态 1 结束

生成:

state1 开始state1.state2.state3.state2 开始从 state1.state2.state3 到 state2 的消息state1.state2.state3.state2 开始从 state1.state2.state3 到 state2 的消息state1.state2.state3.state2 结束state1.state2.state3.state2 结束状态 1 结束

我无法解释原因,也不知道如何解决.

编辑

按照@scipper(第一个答案)的想法,我将演示更新为如下:

<html ng-app="demo28"><头><meta charset="utf-8"/><title>Demo28</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"></脚本><脚本>让 app = angular.module('demo28', ['ui.router']);app.provider('runtimeStates', ['$stateProvider', function ($stateProvider) {this.$get = 函数 () {返回 {新状态:函数(名称,参数){$stateProvider.state(name, param);返回名称;}};};}]);app.config(['$urlRouterProvider', '$stateProvider', function (up, sp) {sp.state('state1', state1);up.otherwise('/state1');}]);让 state1 = {网址:'/state1',控制器:['runtimeStates','$state',函数($rs,$st){this.stateName = $st.current.name;this.createSubState = function(){$st.go($rs.newState($st.current.name + '.state2', state2), {消息:'来自'+$st.current.name+'的消息到state2'});}}],controllerAs: '$ctrl',模板:`<div style="border-style:solid;"><p ng-click="$ctrl.createSubState()" style="cursor: pointer; color: blue;">{{$ctrl.stateName}} begin</p><ui-view></ui-view><p>{{$ctrl.stateName}} end</p>

`};让 state2 = {参数:{消息:''},控制器:['runtimeStates','$transition$','$state',函数($rs,$tr,$st){this.parentMessage = $tr.params().message;this.stateName = $st.current.name;this.createSubState = function(){$st.go($rs.newState($st.current.name + '.state3', state3),{消息:'来自'+$st.current.name+'的消息到state3'});};}],controllerAs: '$ctrl',模板:`<div style="border-style:solid;"><p ng-click="$ctrl.createSubState()" style="cursor: pointer; color: blue;">{{$ctrl.stateName}} begin</p>{{$ctrl.parentMessage}}<ui-view></ui-view><p>{{$ctrl.stateName}} end</p>

`};让 state3 = {参数:{消息:''},控制器:['runtimeStates','$transition$','$state',函数($rs,$tr,$st){this.parentMessage = $tr.params().message;this.stateName = $st.current.name;}],controllerAs: '$ctrl',模板:`<div style="border-style:solid;"><p>{{$ctrl.stateName}} 开始</p>{{$ctrl.parentMessage}}<p>{{$ctrl.stateName}} end</p>

`};<身体><ui-view></ui-view></html>

内容变成:

state1 开始state1.state2.state3 开始从 state1.state2 到 state3 的消息state1.state2.state3 开始从 state1.state2 到 state3 的消息state1.state2.state3 结束state1.state2.state3 结束状态 1 结束

显示stat2的view受state3的影响,应该是使用UI Router的问题.-- 问题仍未解决.

解决方案

它搞砸了,因为 ng-click 的.第一次点击有效,第二次点击触发内部 ng-click,然后是外部.这就是为什么 .state2 总是附加的原因.

编辑

尝试将 $event.stopPropagation() 添加到 ng-click 中:

编辑 2

第二个建议:您只有未命名的视图.通过我的修复,我发现两个内部视图似乎是相同的.我第二次点击后的输出是:

state1 开始state1.state2.state3 开始从 state1.state2 到 state3 的消息state1.state2.state3 开始从 state1.state2 到 state3 的消息state1.state2.state3 结束state1.state2.state3 结束状态 1 结束

编辑 3 - 解决方案

我提到 state2 控制器在状态更改为 state3 后被调用的原因是参数 message.状态参数的每次更改都会导致状态默认解析.如果您不想这样,请将参数指定为动态,例如:

参数:{信息: {价值: '',动态:真实}}

I am creating a vertical navigation panel for my web page (a very basic task). Considering different user roles should have different nav-items against the authorization module on the server, it is desired to create the navigation contents dynamically rather than statically, by getting the data from the server.

I'm trying to use the UI Router to create nested states dynamically (which is really a natural idea called "divide-and-conquer") but got a problem (I described it in another thread but there are only code snippets and cannot demo). I constructed a simple demo here for the problem, in a more general way.

<!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.provider('runtimeStates', ['$stateProvider', function ($stateProvider) {
        this.$get = function () {
          return {
            newState: function (name, param) {
              $stateProvider.state(name, param);
              return name;
            }
          };
        };
      }]);
    
      app.config(['$urlRouterProvider', '$stateProvider', function (up, sp) {
        sp.state('state1', state1);
        up.otherwise('/state1');
      }]);
      
      let state1 = {
        url: '/state1',
        controller: ['runtimeStates', '$state', function ($rs, $st) {
          this.stateName = $st.current.name;
          this.createSubState = function(){
            $st.go($rs.newState($st.current.name + '.state2', state2), {
              message: 'message from ' + $st.current.name + ' to state2'
            });
          }
        }],
        controllerAs: '$ctrl',
        template: `<div ng-click="$ctrl.createSubState()" style="border-style: solid; cursor: pointer;">
          <p>{{$ctrl.stateName}} begin</p>
          <ui-view></ui-view>
          <p>{{$ctrl.stateName}} end</p>
        </div>`
      };
      
      let state2 = {
        params: {message : ''},
        controller: ['runtimeStates', '$transition$', '$state', function ($rs, $tr, $st) {
          this.parentMessage = $tr.params().message;
          this.stateName = $st.current.name;
          this.createSubState = function(){
            $st.go($rs.newState($st.current.name + '.state3', state3),{
              message: 'message from ' + $st.current.name + ' to state3'
            });
          };
        }],
        controllerAs: '$ctrl',
        template: `<div ng-click="$ctrl.createSubState()" style="border-style: solid; cursor: pointer;">
          <p>{{$ctrl.stateName}} begin</p>
          {{$ctrl.parentMessage}}
          <ui-view></ui-view>
          <p>{{$ctrl.stateName}} end</p>
        </div>`
      };
      
      let state3 = {
        params: {message : ''},
        controller: ['runtimeStates', '$transition$', '$state', function ($rs, $tr, $st) {
          this.parentMessage = $tr.params().message;
          this.stateName = $st.current.name;
        }],
        controllerAs: '$ctrl',
        template: `<div style="border-style: solid;">
          <p>{{$ctrl.stateName}} begin</p>
          {{$ctrl.parentMessage}}
          <p>{{$ctrl.stateName}} end</p>
        </div>`
      };
    </script>
  </head>

  <body>
    <ui-view></ui-view>
  </body>

</html>

When the view of state1 populated, I can click on it and generates the view of state2 with the contents expected; but when continuing to click on the view of state2, the generated contents are totally messed. Expected:

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

Generated:

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

I cannot explain why and don't know how to fix.

EDIT

Following the idea of @scipper (the first answer) I updated the demo to bellow:

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

  <head>
    <meta charset="utf-8" />
    <title>Demo28</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('demo28', ['ui.router']);
      
      app.provider('runtimeStates', ['$stateProvider', function ($stateProvider) {
        this.$get = function () {
          return {
            newState: function (name, param) {
              $stateProvider.state(name, param);
              return name;
            }
          };
        };
      }]);
    
      app.config(['$urlRouterProvider', '$stateProvider', function (up, sp) {
        sp.state('state1', state1);
        up.otherwise('/state1');
      }]);
      
      let state1 = {
        url: '/state1',
        controller: ['runtimeStates', '$state', function ($rs, $st) {
          this.stateName = $st.current.name;
          this.createSubState = function(){
            $st.go($rs.newState($st.current.name + '.state2', state2), {
              message: 'message from ' + $st.current.name + ' to state2'
            });
          }
        }],
        controllerAs: '$ctrl',
        template: `<div style="border-style: solid;">
          <p ng-click="$ctrl.createSubState()" style="cursor: pointer; color: blue;">{{$ctrl.stateName}} begin</p>
          <ui-view></ui-view>
          <p>{{$ctrl.stateName}} end</p>
        </div>`
      };
      
      let state2 = {
        params: {message : ''},
        controller: ['runtimeStates', '$transition$', '$state', function ($rs, $tr, $st) {
          this.parentMessage = $tr.params().message;
          this.stateName = $st.current.name;
          this.createSubState = function(){
            $st.go($rs.newState($st.current.name + '.state3', state3),{
              message: 'message from ' + $st.current.name + ' to state3'
            });
          };
        }],
        controllerAs: '$ctrl',
        template: `<div style="border-style: solid;">
          <p ng-click="$ctrl.createSubState()" style="cursor: pointer; color: blue;">{{$ctrl.stateName}} begin</p>
          {{$ctrl.parentMessage}}
          <ui-view></ui-view>
          <p>{{$ctrl.stateName}} end</p>
        </div>`
      };
      
      let state3 = {
        params: {message : ''},
        controller: ['runtimeStates', '$transition$', '$state', function ($rs, $tr, $st) {
          this.parentMessage = $tr.params().message;
          this.stateName = $st.current.name;
        }],
        controllerAs: '$ctrl',
        template: `<div style="border-style: solid;">
          <p>{{$ctrl.stateName}} begin</p>
          {{$ctrl.parentMessage}}
          <p>{{$ctrl.stateName}} end</p>
        </div>`
      };

    </script>
  </head>

  <body>
    <ui-view></ui-view>
  </body>

</html>

and the contents becomes:

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

It shows that the view of stat2 is effected by state3, which should be a problem of using UI Router. -- The problem is still unsolved.

解决方案

It messes up, because of the ng-click's. The first click works, the second click triggers the inner ng-click, then the outer one. That's why .state2 als always appended.

EDIT

Try adding $event.stopPropagation() to the ng-click's:

<div ng-click="$event.stopPropagation(); $ctrl.createSubState()">

EDIT 2

Second suggestion: You only have unnamed views. With my fix I found out, that the two inner views seem to be the same. My output after the second click is:

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

EDIT 3 - SOLUTION

The reason I mentioned that state2 controller gets invoked after the state change to state3 is the parameter message. Every change to a state parameter, causes the state to resolve by default. If you do not want that, specify the parameter as dynamic like:

params: {
  message: {
    value: '',
    dynamic: true
  }
}

这篇关于ui-router 动态创建嵌套状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆