AngularJS UI 路由器 - 在不重新加载状态的情况下更改 url
Posted
技术标签:
【中文标题】AngularJS UI 路由器 - 在不重新加载状态的情况下更改 url【英文标题】:AngularJS UI Router - change url without reloading state 【发布时间】:2014-06-28 09:37:19 【问题描述】:目前我们的项目使用默认的$routeProvider
,我正在使用这个“hack”,在不重新加载页面的情况下更改url
:
services.service('$locationEx', ['$location', '$route', '$rootScope', function($location, $route, $rootScope)
$location.skipReload = function ()
var lastRoute = $route.current;
var un = $rootScope.$on('$locationChangeSuccess', function ()
$route.current = lastRoute;
un();
);
return $location;
;
return $location;
]);
在controller
$locationEx.skipReload().path("/category/" + $scope.model.id).replace();
我正在考虑将routeProvider
替换为ui-router
用于嵌套路由,但在ui-router
中找不到。
有可能 - 对 angular-ui-router
做同样的事情吗?
我为什么需要这个?
让我用一个例子来解释:
创建新类别的路线是/category/new
在 clicking
之后 SAVE 我显示 success-alert
并且我想将路由 /category/new
更改为 /caterogy/23
(23 - 是存储在数据库中的新项目的 ID)
【问题讨论】:
在ui-router中你不必为每个状态定义一个URL,你可以在不改变URL的情况下从一个状态导航到另一个状态 您要更新整个 URL 还是只更新搜索路径?我正在寻找更新搜索路径的解决方案并在这里找到它:***.com/questions/21425378/… @johnathan 真的吗?我很乐意这样做,只显示一个 URL,但$urlRouterProvider.otherwise
似乎在 URL 上运行,而不是在状态上运行。嗯,也许我可以接受 2 个 URL,或者找到其他方法来表明它是一个无效的 URL。
【参考方案1】:
我认为您根本不需要 ui-router。 $location service 的可用文档在第一段中说,“...对 $location 的更改会反映在浏览器地址栏中。”稍后它继续说,“它没有做什么?当浏览器 URL 更改时,它不会导致整个页面重新加载。”
因此,考虑到这一点,为什么不简单地更改 $location.path(因为该方法既是 getter 又是 setter),如下所示:
var newPath = IdFromService;
$location.path(newPath);
documentation 指出路径应始终以正斜杠开头,但如果缺少,则会添加它。
【讨论】:
当我使用ui-router
,并使用$location.path(URL_PATH)
时,页面会自动重新渲染?!
是的,它会重新渲染。我尝试在 $locationChangeStart 上使用 event.preventDefault(),但这也不起作用 - 即它会阻止状态重新呈现,但它也会阻止 url 更新。【参考方案2】:
好的,解决了 :) Angular UI Router 有这个新方法,$urlRouterProvider.deferIntercept() https://github.com/angular-ui/ui-router/issues/64
基本上归结为:
angular.module('myApp', [ui.router])
.config(['$urlRouterProvider', function ($urlRouterProvider)
$urlRouterProvider.deferIntercept();
])
// then define the interception
.run(['$rootScope', '$urlRouter', '$location', '$state', function ($rootScope, $urlRouter, $location, $state)
$rootScope.$on('$locationChangeSuccess', function(e, newUrl, oldUrl)
// Prevent $urlRouter's default handler from firing
e.preventDefault();
/**
* provide conditions on when to
* sync change in $location.path() with state reload.
* I use $location and $state as examples, but
* You can do any logic
* before syncing OR stop syncing all together.
*/
if ($state.current.name !== 'main.exampleState' || newUrl === 'http://some.url' || oldUrl !=='https://another.url')
// your stuff
$urlRouter.sync();
else
// don't sync
);
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.listen();
]);
我认为这个方法目前只包含在 master 版本的 angular ui 路由器中,带有可选参数的那个(也很好,顺便说一句)。它需要使用
从源代码克隆和构建grunt build
也可以通过
从源代码访问这些文档grunt ngdocs
(它们被内置到 /site 目录中)//README.MD 中的更多信息
似乎还有另一种方法可以做到这一点,通过动态参数(我没有使用过)。 很多功劳归功于 nateabele。
作为旁注,这里是 Angular UI Router 的 $stateProvider 中的 可选参数,我结合上述使用:
angular.module('myApp').config(['$stateProvider', function ($stateProvider)
$stateProvider
.state('main.doorsList',
url: 'doors',
controller: DoorsListCtrl,
resolve: DoorsListCtrl.resolve,
templateUrl: '/modules/doors/doors-list.html'
)
.state('main.doorsSingle',
url: 'doors/:doorsSingle/:doorsDetail',
params:
// as of today, it was unclear how to define a required parameter (more below)
doorsSingle: value: null,
doorsDetail: value: null
,
controller: DoorsSingleCtrl,
resolve: DoorsSingleCtrl.resolve,
templateUrl: '/modules/doors/doors-single.html'
);
]);
它的作用是允许解析一个状态,即使其中一个参数丢失。 SEO 是一个目的,可读性是另一个目的。
在上面的示例中,我希望 doorSingle 成为必需参数。目前尚不清楚如何定义这些。不过,它可以与多个可选参数一起使用,所以这不是一个真正的问题。讨论在这里https://github.com/angular-ui/ui-router/pull/1032#issuecomment-49196090
【讨论】:
您的可选参数似乎不起作用。Error: Both params and url specicified in state 'state'
。它在docs 中说这也是无效的用法。有点失望。
您是从 master 构建的吗?请注意,在我添加解决方案时,可选参数仅包含在必须从源代码手动构建的版本中。在 v.0.3 发布之前,它不会包含在版本中。
只是一个简短的说明。感谢 nateabele,可选参数在几天前刚刚发布的 v0.2.11 中可用。
这很令人困惑:如何使用 $urlRouterProvider.deferIntercept();这样我就可以在不重新加载控制器的情况下更新参数?这并没有真正向我展示这一点。在 run 函数中,您可以评估 if 语句以同步或不同步,但我必须使用的只是旧 URL 和新 URL。我怎么知道这是一个我想要做的就是用只有两个 url 来更新参数的实例?逻辑是......(如果旧状态和新状态相同,那么不要重新加载控制器?)我很困惑。
对,我认为这个用例是嵌套状态,我不想重新加载子状态,因此被拦截。我想现在我宁愿使用绝对目标视图来做到这一点,并定义我知道不会改变的状态的视图。不管怎样,这个还是不错的。你会得到完整的 url,也就是说你可以从 url 中猜出状态。还有查询和路径参数等。如果您围绕状态和网址制作应用程序,那将是大量信息。在运行块中,您还可以访问服务等。这是否回答了您的问题?【参考方案3】:
您只需使用 $state.transitionTo
代替 $state.go
。 $state.go
在内部调用 $state.transitionTo
,但会自动将选项设置为 location: true, inherit: true, relative: $state.$current, notify: true
。您可以拨打 $state.transitionTo
并设置 notify: false
。例如:
$state.go('.detail', id: newId)
可以替换为
$state.transitionTo('.detail', id: newId,
location: true,
inherit: true,
relative: $state.$current,
notify: false
)
编辑:正如 fracz 所建议的,它可以简单地是:
$state.go('.detail', id: newId, notify: false)
【讨论】:
那不是“在重新加载状态时保持url”而不是“改变url而不重新加载状态”吗? 对我来说,它适用于以下内容:$state.transitionTo('.detail', id: newId, location: true, inherit: true, relative: $state.$current, notify: false )
所以基本上将 notify 设置为 false 并将 location 设置为 true
Chrome 中的 URL 永远不会更新。最近几个月这里有变化吗?
@ArjendeVries 是的,它按预期工作,但我发现了一个意外行为。当您最终移动到新状态(即 url)时,在玩了许多 transitionTo 方法调用(无需重新加载)之后,它会重新启动旧控制器。
更简单:$state.go('.detail', id: newId, notify: false)
.【参考方案4】:
此设置为我解决了以下问题:
将url从.../
更新为.../123
时,训练控制器没有被调用两次
导航到另一个状态时,训练控制器不会再次被调用
状态配置
state('training',
abstract: true,
url: '/training',
templateUrl: 'partials/training.html',
controller: 'TrainingController'
).
state('training.edit',
url: '/:trainingId'
).
state('training.new',
url: '/trainingId',
// Optional Parameter
params:
trainingId: null
)
调用状态(从任何其他控制器)
$scope.editTraining = function (training)
$state.go('training.edit', trainingId: training.id );
;
$scope.newTraining = function ()
$state.go('training.new', );
;
训练控制器
var newTraining;
if (!!!$state.params.trainingId)
// new
newTraining = // create new training ...
// Update the URL without reloading the controller
$state.go('training.edit',
trainingId : newTraining.id
,
location: 'replace', // update url and replace
inherit: false,
notify: false
);
else
// edit
// load existing training ...
【讨论】:
我试图使用类似的策略,但我的控制器从未从编辑页面获得 trainigId 的值,我可能在那里遗漏了什么?我尝试直接从 URL 并使用 ui-sref 访问编辑页面。我的代码和你的一模一样。 这对我有用,是迄今为止最清晰的解决方案【参考方案5】:在这个问题上花了很多时间后,这就是我的工作
$state.go('stateName',params,
// prevent the events onStart and onSuccess from firing
notify:false,
// prevent reload of the current state
reload:false,
// replace the last record when changing the params so you don't hit the back button and get old params
location:'replace',
// inherit the current params on the url
inherit:true
);
【讨论】:
其他解决方案与路由提供者有关。这个解决方案适用于我的情况,比如我使用 $stateProvider 而不是使用 $routeProvider。 @eeejay 基本上这个问题只针对ui-router
,你怎么能这么说,其他解决方案适用于$routerProvider
,$routeProvider
和$stateProvider
在架构上完全不同。 .
如果我们不希望后退按钮使用这个怎么办?我的意思是,在后按时转到上一个 state/url ,而不是使用不同参数的同一状态
我猜,通过这个解决方案,浏览器返回将无法正常工作,因为我们正在更改状态而不让 ui-router 知道它
我试过这个,看起来每次我使用 $state.go
时都会在我的组件上调用 $onInit()
。对我来说似乎不是 100% 好的。【参考方案6】:
我在很久以前就这样做了
$stateProvider
.state(
'home',
url: '/home',
views:
'':
templateUrl: Url.resolveTemplateUrl('shared/partial/main.html'),
controller: 'mainCtrl'
,
)
.state('home.login',
url: '/login',
templateUrl: Url.resolveTemplateUrl('authentication/partial/login.html'),
controller: 'authenticationCtrl'
)
.state('home.logout',
url: '/logout/:state',
controller: 'authenticationCtrl'
)
.state('home.reservationChart',
url: '/reservations/?vw',
views:
'':
templateUrl: Url.resolveTemplateUrl('reservationChart/partial/reservationChartContainer.html'),
controller: 'reservationChartCtrl',
reloadOnSearch: false
,
'viewVoucher@home.reservationChart':
templateUrl: Url.resolveTemplateUrl('voucher/partial/viewVoucherContainer.html'),
controller: 'viewVoucherCtrl',
reloadOnSearch: false
,
'addEditVoucher@home.reservationChart':
templateUrl: Url.resolveTemplateUrl('voucher/partial/voucherContainer.html'),
controller: 'voucherCtrl',
reloadOnSearch: false
,
reloadOnSearch: false
)
【讨论】:
【参考方案7】:如果您只需要更改 url 但阻止更改状态:
更改位置(如果要在历史记录中替换,请添加 .replace):
this.$location.path([Your path]).replace();
防止重定向到您的状态:
$transitions.onBefore(, function($transition$)
if ($transition$.$to().name === '[state name]')
return false;
);
【讨论】:
【参考方案8】:试试这样的
$state.go($state.$current.name, ... $state.params, 'key': newValue, notify: false)
【讨论】:
【参考方案9】:打电话
$state.go($state.current, myParam: newValue, notify: false);
仍会重新加载控制器,这意味着您将丢失状态数据。
为避免这种情况,只需将参数声明为动态:
$stateProvider.state(
name: 'myState',
url: '/my_state?myParam',
params:
myParam:
dynamic: true, // <----------
,
...
);
那你甚至不需要notify
,只需调用
$state.go($state.current, myParam: newValue)
足够了。尼托!
来自documentation:
当
dynamic
为true
时,更改参数值将 不会导致进入/退出状态。决议不会 重新获取,也不会重新加载视图。这对构建很有用 当参数值发生变化时,组件会自行更新的 UI。
【讨论】:
以上是关于AngularJS UI 路由器 - 在不重新加载状态的情况下更改 url的主要内容,如果未能解决你的问题,请参考以下文章
AngularJS的ui-router第一次点击激活路由加载页面,再次点击就没用,如何实现每次点击都激活加载路由一次
Angularjs ui router,路由嵌套 父controller执行问题
AngularJS、ocLazyLoad 和 ui-router:如何在不将“状态”集中在 app.js 主类上的情况下按需加载