Abstract
Hi, I'm using angular + ui-router in my project, I have huge amount of nested states and different views that in turn contain huge amount of different inputs, a user fills these inputs incrementally step by step.
嗨,我在我的项目中使用了角度+ ui-router,我有大量的嵌套状态和不同的视图,它们反过来包含大量不同的输入,用户一步一步地递增地填充这些输入。
The problem
Sometimes users require additional info that is located on the previous step, and browsers "back" button helps the user to sneak peek into that data, but as soon as the user presses it, the info he already entered is lost due to state transition, which is obviously a bad thing.
有时用户需要位于上一步的附加信息,浏览器“后退”按钮帮助用户偷窥数据,但是一旦用户按下它,他已经输入的信息就会由于状态转换而丢失,这显然是一件坏事。
Strategy
In order to overcome described problem I have the following plan:
为了解决上述问题,我有以下计划:
- Associate each user's "navigation" (guess this is a proper term) with a random id
- 将每个用户的“导航”(猜测这是一个合适的术语)与一个随机的id关联起来。
- To prevent scope-inheritance-and-serialization issues, instead of putting viewmodel into
$scope
use ordinary javascript object that will be storing immediate values that are bound to UI. - 为了防止作用域继承和序列化问题,不要将viewmodel放入$scope中,使用普通javascript对象来存储绑定到UI的当前值。
- Add watcher to look for changes on that "storage object"
- 添加监视程序以查找对“存储对象”的更改
- As soon as the change spotted, serialize the object and persist it
- 一旦更改被发现,将对象序列化并持久化
Explanations
Why do we need a random parameter in URL?
为什么我们需要一个URL中的随机参数?
We don't want to store all data in URL, since there might be quite some amount of data that wont fit into URL. So in order to provide the guarantees the URL won't break, we put only small random GUID/UUID into it that later allows obtaining the data associated with current "navigation" by this random GUID/UUID.
我们不希望将所有数据存储在URL中,因为可能有相当数量的数据不能放入URL中。因此,为了保证URL不会被破坏,我们只在其中加入了小的随机GUID/UUID,以便以后可以通过这个随机的GUID/ uid获取与当前“导航”相关的数据。
The storage
There are multitude of storage scenarios available out there: LocalStorage
, IndexedDB
, WebSQL
, Session Storage
, you name it, but due to their cross-tab, cross-browser, browser-specific nature it would be hard to manipulate and manage all of the data that gets into the storage. The implementation will be buggy / might require server-side support.
有很多存储场景可供选择:LocalStorage、IndexedDB、WebSQL、会话存储,您可以命名它们,但是由于它们的跨标签、跨浏览器、特定于浏览器的特性,很难操作和管理进入存储的所有数据。实现将是错误的/可能需要服务器端支持。
So the most elegant storage strategy for this scenario would be storing data in special window.name
variable which is capable of storing data in-between requests. So the data is safe until you close your tab.
因此,这种场景最优雅的存储策略是将数据存储在特殊的window.name变量中,该变量可以在请求之间存储数据。所以数据是安全的,直到你关闭标签。
The Question
On behalf of everything written above, I have the root view called "view" that has a state parameter id
(this is the random GUID/UUID)
对于上面所写的内容,我有一个名为“view”的根视图,它具有一个状态参数id(这是随机的GUID/ uid)
$stateProvider.state('view', {
url: '/view/{id}',
controller: 'view',
templateUrl: 'views/view.html'
});
All of the other views derive from this view, is there way to make ui-sref
directive to automatically inject a random GUID/UUID into id
state parameter of my root view, instead of writing each time ui-sref
's like:
所有其他的视图都来自这个视图,是否有办法让ui-sref指令自动将一个随机的GUID/UUID注入到根视图的id状态参数中,而不是每次写入ui-sref,比如:
<a ui-sref="view({id:guid()}).someNestedView({someNestedParam: getParam()})"
I would like to have something like:
我想要的是:
<a ui-sref="view.someNestedView({someNestedParam: getParam()})"
1 个解决方案
#1
2
The AOP and Decorator pattern are the answer. The comprehensive description could be found here:
AOP和Decorator模式就是答案。综合描述可以在这里找到:
Experiment: Decorating Directives by Jesus Rodriguez
Similar solution as described below, could be observed:
类似的解决方案如下所述:
Changing the default behavior of $state.go() in ui.router to reload by default
How that would work? There is a link to working example
这将如何工作的呢?这里有一个工作示例的链接
In this case, we do not solve from which source the random GUID comes from. Let's just have it in runtime:
在这种情况下,我们不解决随机GUID来自哪个源。让我们把它放在运行时:
var guidFromSomeSource = '70F81249-2487-47B8-9ADF-603F796FF999';
Now, we can inject an Decorator like this:
现在,我们可以注入这样的Decorator:
angular
.module('MyApp')
.config(function ($provide) {
$provide.decorator('$state', function ($delegate) {
// let's locally use 'state' name
var state = $delegate;
// let's extend this object with new function
// 'baseGo', which in fact, will keep the reference
// to the original 'go' function
state.baseGo = state.go;
// here comes our new 'go' decoration
var go = function (to, params, options) {
params = params || {};
// only in case of missing 'id'
// append our random/constant 'GUID'
if (angular.isUndefined(params.id)) {
params.id = guidFromSomeSource;
}
// return processing to the 'baseGo' - original
this.baseGo(to, params, options);
};
// assign new 'go', right now decorating the old 'go'
state.go = go;
return $delegate;
});
})
Code should be self explanatory, check it in action here
代码应该是自解释的,在这里检查它。
#1
2
The AOP and Decorator pattern are the answer. The comprehensive description could be found here:
AOP和Decorator模式就是答案。综合描述可以在这里找到:
Experiment: Decorating Directives by Jesus Rodriguez
Similar solution as described below, could be observed:
类似的解决方案如下所述:
Changing the default behavior of $state.go() in ui.router to reload by default
How that would work? There is a link to working example
这将如何工作的呢?这里有一个工作示例的链接
In this case, we do not solve from which source the random GUID comes from. Let's just have it in runtime:
在这种情况下,我们不解决随机GUID来自哪个源。让我们把它放在运行时:
var guidFromSomeSource = '70F81249-2487-47B8-9ADF-603F796FF999';
Now, we can inject an Decorator like this:
现在,我们可以注入这样的Decorator:
angular
.module('MyApp')
.config(function ($provide) {
$provide.decorator('$state', function ($delegate) {
// let's locally use 'state' name
var state = $delegate;
// let's extend this object with new function
// 'baseGo', which in fact, will keep the reference
// to the original 'go' function
state.baseGo = state.go;
// here comes our new 'go' decoration
var go = function (to, params, options) {
params = params || {};
// only in case of missing 'id'
// append our random/constant 'GUID'
if (angular.isUndefined(params.id)) {
params.id = guidFromSomeSource;
}
// return processing to the 'baseGo' - original
this.baseGo(to, params, options);
};
// assign new 'go', right now decorating the old 'go'
state.go = go;
return $delegate;
});
})
Code should be self explanatory, check it in action here
代码应该是自解释的,在这里检查它。