The transition of the UI router is preempted when moving the state tree with several views

I am trying to manage my admin page with several subordinate states with several views: admin, admin.header, admin.leftPanel, admin.main, admin.tail. In the header, leftPanel, main and tail, I use $ state.go for my subordinate states, respectively, to display their contents. I am writing simple simple code to demonstrate this problem.

Demo State Model:

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

Code ( 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> 

Expected Result:

 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 

Actual result:

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

Console output: console output

+1
source share
2 answers

Turns out I just hit the wall.

The idea of ​​including page content in the states and location of the user interface routers while visiting these states in a single software execution is absolutely wrong. In particular, you cannot simultaneously display two representations of sister states.

UI Router is designed for routing with mouse clicks. Despite the fact that the collected documents are hinting strongly, we can transfer our full page on the state tree to place all the content, but this is not always the case. As long as the application logic goes into another state that is not depressed due to the state, exits from the state before entering the state and its created representation of the exit state are completely deleted.

The Bellow code initially tries to prove my concept below (improving the design in the original question, since I realized that there is no break-point / resume design in state) and solve my problem, but it turns out to show an example of the opposite - impossibility under the wrong idea.

Concept

  • Define the states and their hierarchy and patterns for the structure of the page layout;
  • Make a state tree;
  • Make a traverse
  • Walk the path of $ state.go to each state to expand and display the layout.

Code ( 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 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> 

Result (Firefox 57.0.1)

When entering a page: enter image description here

After clicking and closing the warning: enter image description here

The above process showed that state1.state2 was executed and laid out (but not yet rated / displayed on angular), as we see in the first figure. There was no exit at this point, because the onExit warning blocked the process. After the alert call sign was closed, the state ended and the view was completely deleted.

There is a sticky-state designed for a specific purpose on the page, but since I tried, this does not work here. It remembers the last visited stick states, but scans of outgoing states are always deleted.

Now I'm trying to use the UI Router only as a notation tool for routing. But I have to be very conscious so as not to come across the idea that the UI Router can be used as a general tool for page layout, for example, to extend the angular component. But it can be difficult: I can’t come up with the right way to use the UI Router at the moment. In the case of multiple views, if any two representations of brothers and sisters have their own sub-states, I must be very careful, because visiting one of them goes beyond the other - they are exceptional. It makes me think that it’s not worth its complexity.

In most cases, when navigating, you need to delete views when exiting, I suggest changing the UI Router interface and giving the ability to save views for more flexibility. It may be more difficult than the first thought, but it should be possible.

It is also advisable to cache all the “last visible” parameters for each state (not only for sticky states) so that we can easily return to them. You can argue about a precedent, but we cannot imagine how people will use the tool and should not limit opportunities.

It is also advisable to provide a tool for hooks of the full life cycle to the state base (now they have only OnEnter and onExit).

0
source

You must use the stateHelper module created by @marklagendijk.

Read this article in the link below on nested states for more options if you do not want to use the above module

Nested States

plunkr

 angular.module('app').config([ '$urlRouterProvider', 'stateHelperProvider', function($urlRouterProvider, stateHelperProvider) { stateHelperProvider.state({ name: 'state1', template: '<ui-view/>', abstract: true, resolve: { // haven't got to this point yet :-/ }, children: [{ name: 'state2', controller:['$state','$q', function ( $st,$q) { this.stateName = $st.current.name; // $st.transitionTo('state1.state2', {message: 'message from ' + $st.current.name + ' to ' + $st.current.name + '.state2'}); }], controllerAs: '$ctrl', url : '/', template: `<br>{{$ctrl.stateName}} begin <br>message from {{$ctrl.stateName}} to {{$ctrl.stateName}}.state3 <br>{{$ctrl.stateName}} end` },{ name: 'state3', url : '/', controller:['$state','$q', function ( $st,$q) { this.stateName = $st.current.name; // $st.transitionTo('state1.state2', {message: 'message from ' + $st.current.name + ' to ' + $st.current.name + '.state2'}); }], controllerAs: '$ctrl', template: `<br>{{$ctrl.stateName}} begin <br>message from {{$ctrl.stateName}} to {{$ctrl.stateName}}.state3 <br>{{$ctrl.stateName}} end` }] });}]); 
0
source

Source: https://habr.com/ru/post/1241509/


All Articles