ui.router 默认参数值导致页面刷新错误
ui.router default param value causes page refresh error
我正在试验 ui.router 让一些导航选项卡保持状态。当我意识到指定默认参数值会在我刷新页面时提供一些 unexpected/buggy/weird 行为时,我几乎要拥有我想要的一切。
我有两个参数,stateNum
(1 或 2)和 subState
(A、B 或 C)。 URL 的格式为 /stateTabs/state/:stateNum/sub/:subState
。有问题的状态定义是
$stateProvider.state("stateTabs", {
url: "/stateTabs"
,controller: "StateTabsCtrl"
// ,params: { // default params
// stateNum: "2"
// ,subState: "B"
// }
,templateUrl: "tplStateTabs"
});
$stateProvider.state("stateTabs.state", {
url: "/state/:stateNum"
});
$stateProvider.state("stateTabs.state.sub", {
url: "/sub/:subState"
});
如果 params
属性 被启用,刷新页面将导致 stateNum
等于 URL 的 <stateNum>/sub/<subState>
,并且 subState
等于默认值 "x"
。好像是在做贪心匹配。单击任何选项卡将 return 恢复正常功能。
我希望在刷新时,ui.router 会看到一个 URL,比如 "stateTabs/state/2/sub/B",并将其解析为正确的 $stateParams。这是一个错误还是我做错了什么?
我最终想要完成的是能够 ui-sref
仅 stateTabs
并将默认设置为预定义的 stateNum+subState。
完整的代码片段如下,但需要将其复制到本地文件才能刷新并触发错误。
var StateTabs = (function ()
{
"use strict";
var module = angular.module("StateTabs", ["ng", "ui.router"]);
module.config(["$stateProvider", "$urlRouterProvider",
function ($stateProvider, $urlRouterProvider)
{
$urlRouterProvider.otherwise("/");
// controller not instantiated if template is not defined
$stateProvider.state("index", {
url: "/"
});
$stateProvider.state("stateTabs", {
url: "/stateTabs"
,controller: "StateTabsCtrl"
/*
* If the following property is enabled,
* refreshing the page will cause stateNum
* to equal the "/<stateNum>/sub/<subState>"
* from the URL and subState to equal the
* default of "x". Click any tab will return
* to normal functionality.
*/
// ,params: { // default params
// stateNum: "3"
// ,subState: "x"
// }
,templateUrl: "tplStateTabs"
});
$stateProvider.state("stateTabs.state", {
url: "/state/:stateNum"
});
$stateProvider.state("stateTabs.state.sub", {
url: "/sub/:subState"
});
}
]);
module.controller("StateTabsCtrl", ["$scope",
function ($scope)
{
$scope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams) {
$scope.stateParams = toParams;
});
}
]);
return module;
})();
div#stateTabs ul.tabbed-nav,
div#stateTabs ul.tabbed-sub-nav {
display: flex;
flex-wrap: nowrap;
align-items: stretch;
padding: 0;
margin: 0;
font-size: 16px;
text-align: center;
line-height: 1.1;
}
div#stateTabs ul.tabbed-nav li,
div#stateTabs ul.tabbed-sub-nav li {
padding: 1rem 2rem;
margin-right: 1px;
display: flex;
flex-flow: column;
justify-content: center;
}
div#stateTabs ul.tabbed-nav li:last-child,
div#stateTabs ul.tabbed-sub-nav li:last-child {
margin-right: 0;
}
div#stateTabs ul.tabbed-nav li a,
div#stateTabs ul.tabbed-sub-nav li a {
color: inherit;
text-decoration: none;
}
div#stateTabs ul.tabbed-nav {
color: white;
padding: 0;
margin: 0;
}
div#stateTabs ul.tabbed-nav li {
background-color: black;
}
div#stateTabs ul.tabbed-sub-nav {
justify-content: flex-start;
color: inherit;
position: relative;
}
div#stateTabs ul.tabbed-sub-nav li {
background-color: silver;
z-index: 10;
}
div#stateTabs ul.tabbed-nav li.selected {
color: black;
background-color: #4dff4d !important;
}
div#stateTabs ul.tabbed-sub-nav li.selected {
background-color: #269abc !important;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AngularJS Sandbox</title>
</head>
<body ng-app="StateTabs">
<h1>AngularJS Sandbox</h1>
<div ui-view><a href ui-sref="stateTabs">begin</a></div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.13/angular-ui-router.js"></script>
<script type="text/ng-template" id="tplStateTabs">
<div id="stateTabs">
<ul class="tabbed-nav">
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: '1', subState: stateParams.subState })">state 1</a></li>
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: '2', subState: stateParams.subState })">state 2</a></li>
</ul>
<ul class="tabbed-sub-nav">
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: stateParams.stateNum, subState: 'A' })">substate A</a></li>
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: stateParams.stateNum, subState: 'B' })">substate B</a></li>
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: stateParams.stateNum, subState: 'C' })">substate C</a></li>
</ul>
<div>
<p>$stateParams: <span style="font-family: monospace">{{ stateParams }}</span></p>
<p>stateNum: <span style="font-family: monospace">{{ stateParams.stateNum }}</span></p>
<p>subState: <span style="font-family: monospace">{{ stateParams.subState }}</span></p>
</div>
</div>
</script>
</body>
</html>
这里的解决方案并不那么复杂 - 只需让 params
属于状态,在它们被定义的地方。有个workgin plunker
$stateProvider.state("stateTabs", {
url: "/stateTabs"
,controller: "StateTabsCtrl"
,templateUrl: "tplStateTabs.html"
// PARAMS do not belong here, because there is nothing related in URL
});
$stateProvider.state("stateTabs.state", {
url: "/state/:stateNum",
params: { // default params
stateNum: "3" // here belongs stateNum
}
});
$stateProvider.state("stateTabs.state.sub", {
url: "/sub/:subState",
params: { // default params
subState: "x" // here can be subState
}
});
实际检查 here
我正在试验 ui.router 让一些导航选项卡保持状态。当我意识到指定默认参数值会在我刷新页面时提供一些 unexpected/buggy/weird 行为时,我几乎要拥有我想要的一切。
我有两个参数,stateNum
(1 或 2)和 subState
(A、B 或 C)。 URL 的格式为 /stateTabs/state/:stateNum/sub/:subState
。有问题的状态定义是
$stateProvider.state("stateTabs", {
url: "/stateTabs"
,controller: "StateTabsCtrl"
// ,params: { // default params
// stateNum: "2"
// ,subState: "B"
// }
,templateUrl: "tplStateTabs"
});
$stateProvider.state("stateTabs.state", {
url: "/state/:stateNum"
});
$stateProvider.state("stateTabs.state.sub", {
url: "/sub/:subState"
});
如果 params
属性 被启用,刷新页面将导致 stateNum
等于 URL 的 <stateNum>/sub/<subState>
,并且 subState
等于默认值 "x"
。好像是在做贪心匹配。单击任何选项卡将 return 恢复正常功能。
我希望在刷新时,ui.router 会看到一个 URL,比如 "stateTabs/state/2/sub/B",并将其解析为正确的 $stateParams。这是一个错误还是我做错了什么?
我最终想要完成的是能够 ui-sref
仅 stateTabs
并将默认设置为预定义的 stateNum+subState。
完整的代码片段如下,但需要将其复制到本地文件才能刷新并触发错误。
var StateTabs = (function ()
{
"use strict";
var module = angular.module("StateTabs", ["ng", "ui.router"]);
module.config(["$stateProvider", "$urlRouterProvider",
function ($stateProvider, $urlRouterProvider)
{
$urlRouterProvider.otherwise("/");
// controller not instantiated if template is not defined
$stateProvider.state("index", {
url: "/"
});
$stateProvider.state("stateTabs", {
url: "/stateTabs"
,controller: "StateTabsCtrl"
/*
* If the following property is enabled,
* refreshing the page will cause stateNum
* to equal the "/<stateNum>/sub/<subState>"
* from the URL and subState to equal the
* default of "x". Click any tab will return
* to normal functionality.
*/
// ,params: { // default params
// stateNum: "3"
// ,subState: "x"
// }
,templateUrl: "tplStateTabs"
});
$stateProvider.state("stateTabs.state", {
url: "/state/:stateNum"
});
$stateProvider.state("stateTabs.state.sub", {
url: "/sub/:subState"
});
}
]);
module.controller("StateTabsCtrl", ["$scope",
function ($scope)
{
$scope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams) {
$scope.stateParams = toParams;
});
}
]);
return module;
})();
div#stateTabs ul.tabbed-nav,
div#stateTabs ul.tabbed-sub-nav {
display: flex;
flex-wrap: nowrap;
align-items: stretch;
padding: 0;
margin: 0;
font-size: 16px;
text-align: center;
line-height: 1.1;
}
div#stateTabs ul.tabbed-nav li,
div#stateTabs ul.tabbed-sub-nav li {
padding: 1rem 2rem;
margin-right: 1px;
display: flex;
flex-flow: column;
justify-content: center;
}
div#stateTabs ul.tabbed-nav li:last-child,
div#stateTabs ul.tabbed-sub-nav li:last-child {
margin-right: 0;
}
div#stateTabs ul.tabbed-nav li a,
div#stateTabs ul.tabbed-sub-nav li a {
color: inherit;
text-decoration: none;
}
div#stateTabs ul.tabbed-nav {
color: white;
padding: 0;
margin: 0;
}
div#stateTabs ul.tabbed-nav li {
background-color: black;
}
div#stateTabs ul.tabbed-sub-nav {
justify-content: flex-start;
color: inherit;
position: relative;
}
div#stateTabs ul.tabbed-sub-nav li {
background-color: silver;
z-index: 10;
}
div#stateTabs ul.tabbed-nav li.selected {
color: black;
background-color: #4dff4d !important;
}
div#stateTabs ul.tabbed-sub-nav li.selected {
background-color: #269abc !important;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AngularJS Sandbox</title>
</head>
<body ng-app="StateTabs">
<h1>AngularJS Sandbox</h1>
<div ui-view><a href ui-sref="stateTabs">begin</a></div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.13/angular-ui-router.js"></script>
<script type="text/ng-template" id="tplStateTabs">
<div id="stateTabs">
<ul class="tabbed-nav">
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: '1', subState: stateParams.subState })">state 1</a></li>
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: '2', subState: stateParams.subState })">state 2</a></li>
</ul>
<ul class="tabbed-sub-nav">
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: stateParams.stateNum, subState: 'A' })">substate A</a></li>
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: stateParams.stateNum, subState: 'B' })">substate B</a></li>
<li ui-sref-active="selected"><a ui-sref="stateTabs.state.sub({ stateNum: stateParams.stateNum, subState: 'C' })">substate C</a></li>
</ul>
<div>
<p>$stateParams: <span style="font-family: monospace">{{ stateParams }}</span></p>
<p>stateNum: <span style="font-family: monospace">{{ stateParams.stateNum }}</span></p>
<p>subState: <span style="font-family: monospace">{{ stateParams.subState }}</span></p>
</div>
</div>
</script>
</body>
</html>
这里的解决方案并不那么复杂 - 只需让 params
属于状态,在它们被定义的地方。有个workgin plunker
$stateProvider.state("stateTabs", {
url: "/stateTabs"
,controller: "StateTabsCtrl"
,templateUrl: "tplStateTabs.html"
// PARAMS do not belong here, because there is nothing related in URL
});
$stateProvider.state("stateTabs.state", {
url: "/state/:stateNum",
params: { // default params
stateNum: "3" // here belongs stateNum
}
});
$stateProvider.state("stateTabs.state.sub", {
url: "/sub/:subState",
params: { // default params
subState: "x" // here can be subState
}
});
实际检查 here