我认为我的目标不能与AngularUI路由器一起工作 - 但我会把它放在这里以防万一有人可以证明我错了或有替代解决方案或解决同样问题的解决方法。
目标
我想显示更改网址的模态窗口,但可以从我的应用程序中的任何位置打开 - 无论哪个父状态当前处于活动状态。具体来说,我希望更改URL,以便在按下浏览器/设备后退按钮时,关闭模式(即应用程序将返回到他们使用的任何父状态)。用户可以在使用应用程序时随时打开这样的模式(例如,可以从应用程序的主菜单栏访问帮助窗口)。
我真正想要做的是将模态状态复制并粘贴为每个可能的父状态的子项(即将帮助状态注册为每个用户配置文件的子项) /搜索结果/ home / etc ...)。如果应用程序中只有一个这样的模态,那么可能是一种可接受的方法 - 但是当你开始将几个全局可访问的模态子状态引入应用程序时,多个子状态注册开始变为真实问题
为了更清楚地说明,这是一个用户故事:
在上面的故事中,我认为导致后向事件关闭模态的唯一方法是将模态绑定到AngularUI路由器的状态转换。用户将从搜索结果状态(url:/search-results
)转到帮助状态(url:/search-results?help
) - 但是,在另一种情况下,他们可能会从用户个人资料状态转到(url:{{ 1}})到帮助状态(url:/profile/123
)。这里的关键是,帮助没有直接注册为两者搜索结果和配置文件状态的子项,但不知何故独立地作为一种孤立状态可能可能应用于任何父项
替代目标
这不是我首选的解决方案。如果可以在不更改URL的情况下使浏览器/设备返回按钮关闭模态,那么我可以使这些模态独立于AngularUI路由器工作 - 但我不喜欢这样的方法,这意味着对不同类型的观点采用不一致的开发方法(谁知道,也许在未来我们将决定其中一个模态窗口本身应该是一流的状态,这需要从一个改变接近另一个 - 这是不可取的)。我认为这是一种不可靠的方法,因为根据我的经验,处理后退事件并非易事。
这实际上对许多情况都很有用(例如,用户可以点击返回以关闭子菜单或上下文菜单),我只是不认为它是技术上可行的解决方案 - 但是随意证明我错了。 ; - )
备注
提前感谢您提供的任何帮助!
修改 1.更新了用户故事说明,以便更清晰地包含具体的URL /状态示例。
答案 0 :(得分:1)
嗯,对于有类似需求的人,我找到了一个简单的解决方案,它基本上超出了UI路由器的整个路由机制。
首先,我认为应该可以在即将发布的0.3版本中使用deferIntercept
功能,详见in this SO answer。但是,我的解决方案采用了不同的方法。我没有使用查询参数来识别这些孤立的视图(即?help
),而是使用url片段标识符(即#help
)。这是有效的,因为路由机制似乎忽略了哈希符号之后的任何内容。
在我成功完成这项任务之前,我确实遇到了一些问题 - 特别是在$location
服务中处理非html5模式时。据我了解,在片段标识符中包含哈希符号(即,网址不能包含两个#
符号),技术上非法非法,因此它带来一些风险,但是根据我的测试,似乎浏览器不会抱怨太多。)
我的解决方案涉及拥有hashRouter
服务,该服务管理将查询数据序列化和反序列化到片段标识符和从片段标识符反序列化的作业,并监视$locationChangeSuccess
事件以处理URL中的外部更改(即,按下浏览器或设备的后退和前进按钮。)
这是我服务的简化版本:
hashRouter.$inject = [
'$rootScope',
'$location'
];
function hashRouter($rootScope, $location) {
var service = this,
hashString = $location.hash(),
hash = fromHashString(hashString);
$rootScope.$on('$locationChangeSuccess', function (e, newUrl) {
var newHashString = getHashSection(newUrl);
if (newHashString != hashString) {
var newHash = fromHashString(newHashString);
service.hash(newHash.name, newHash.params);
}
});
service.hash = function (name, params) {
var oldHash = hash,
oldHashString = hashString;
hash = { name: name || '', params: params || {} };
hashString = toHashString(hash);
if (hashString !== oldHashString) {
var oldHashExists = oldHashString.length > 0;
if (oldHashExists) {
$rootScope.$broadcast('hashRouteRemoved', oldHash);
}
if (hashString.length > 0) {
$rootScope.$broadcast('hashRouteAdded', hash);
}
$location.hash(hashString);
if (oldHashExists) {
$location.replace();
}
}
};
return service;
function toHashString(data) {
var newHashString = '';
var name = data.name;
if (!!name) {
newHashString += encodeURIComponent(name);
}
var params = data.params;
if (!!params) {
var paramList = [];
for (var prop in params) {
var key = encodeURIComponent(prop),
value = params.hasOwnProperty(prop) ? encodeURIComponent(params[prop].toString()) : '';
paramList.push(key + '=' + value);
}
if (paramList.length > 0) {
newHashString += ':' + paramList.join('&');
}
}
return newHashString;
}
function fromHashString(urlHash) {
var parsedHash = {
name: '',
params: {}
};
if (!!urlHash && urlHash.length > 0) {
if (urlHash.indexOf(':') !== -1) {
var hashSegments = urlHash.split(':');
parsedHash.name = decodeURIComponent(hashSegments[0]);
var querySegments = hashSegments[1].split('&');
for (var i = 0; i < querySegments.length; i++) {
var pair = querySegments[i].split('=');
parsedHash.params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]) || null;
}
} else {
parsedHash.name = decodeURIComponent(urlHash);
}
}
return parsedHash;
}
function getHashSection(url) {
if (url.indexOf('#') === -1 || (url.indexOf('#!') !== -1 && url.indexOf('#') === url.lastIndexOf('#'))) {
return '';
}
var urlSegments = url.split('#');
return urlSegments[urlSegments.length - 1];
}
}
angular.module('myApp').service('hashRouter', hashRouter);
有关服务的注意事项有几点:
#!
而不是#
)的 bang 部分。hashRouteAdded
和hashRouteRemoved
事件。希望如果其他人需要和我在这里做的一样,这可以节省一些时间: - )
答案 1 :(得分:0)
您是否可以使用单个州作为应用中所有内容的父级?我在角度应用程序中做同样的事情。
$stateProvider
//root route
.state('app', {
url: '/',
templateUrl: '/scripts/app/app/views/app.html',
controller: 'appController',
resolve: {
//resolve any app wide data here
}
});
然后你可以将你的模态作为这个状态的孩子。这样您就可以始终转换回此路线以返回到应用的默认状态(当您的模态关闭时)。 以这种方式执行操作的另一个好处是,您可以使用此路由的视图作为布局,以放置任何不会在页面之间更改的标记(标题,侧边栏等)。