我构建了一个可停靠的面板解决方案,我想将布局存储到localStorage
,因此我可以在重新加载页面时再次使用它。这实际上就像我的localhost上的魅力一样。这些是最重要的模型以及我如何使用它们:
export class DockTreeNode {
parent: DockTreeContainer;
// methods..
}
export class DockTreePanel extends DockTreeNode {
type: Type<DockablePanel>;
args: DockArguments;
movable: boolean;
}
export class DockTreeContainer extends DockTreeNode {
private orientation: Orientation;
private left: DockTreeNode;
private right: DockTreeNode;
}
dockTree: DockTreeNode = new DockTreeContainer(
new DockTreePanel(
TestPanelComponent,
null,
false
),
new DockTreePanel(
BovenbouwingComponent,
null,
true
),
Orientation.horizontal
);
保存和恢复布局的代码如下:
private typeMap: Map<string, Type<DockablePanel>> = new Map();
/**
* Saving is pretty damn simple
*/
public saveDockTree(): void {
const saveString = JSON.stringify(this.dockTree, (key, value) => {
if (key === 'parent') {
return null;
} else if (key === 'type') {
return value.name;
} else {
return value;
}
});
localStorage.setItem(this.localStorageKey, saveString);
}
/**
* This method will check local storage for our stored tree and parse everything
* and be all kinds of cool on local storage.
*/
public setDefaultDockTree(dockTree: DockTreeNode): void {
this.defaultDockTree = dockTree;
try {
// Retrieve the stored item
const storageItem = localStorage.getItem(this.localStorageKey);
if (storageItem) {
const stored = JSON.parse(storageItem, (key, value) => {
if (key === 'type') {
if (!this.typeMap.has(value)) {
throw new Error('Layout types don\t match.');
}
return this.typeMap.get(value);
} else {
return value;
}
});
// Convert to real dockTree
const tree: DockTreeNode = this.JSONtoDockTree(stored);
this.dockTree = tree;
} else {
throw new Error('Could not find layout');
}
} catch (e) {
// Anything wrong? Reset the dockTree layout to default
this.dockTree = this.defaultDockTree;
this.saveDockTree();
console.log('Could not parse stored layout. Resetting local storage');
}
}
/**
* I think this can be done better, but that's not the problem here.
*/
private JSONtoDockTree(tree: any): DockTreeNode {
let node: DockTreeNode;
if (tree.left && tree.right) {
const left = this.JSONtoDockTree(tree.left);
const right = this.JSONtoDockTree(tree.right);
node = new DockTreeContainer(left, right, tree.orientation);
} else if (tree.type) {
node = new DockTreePanel(tree.type, tree.args, tree.movable);
} else {
throw new Error('Node is not a DockTreePanel or DockTreeContainer.');
}
return node;
}
现在在本地存储中,我保存的dockTree看起来像这样:
{
"left": {
"type": "TestPanelComponent",
},
"right": {
"type": "SomePanelComponent",
},
// More
}
但是当我将它提交到svn存储库并且Jenkins使用ng build --prod
构建并启动运行构建的Docker容器时,类型的保存值将是完全无意义的:
{
"left": {
"type": "",
},
"right": {
"type": "n",
}
// More stuff
}
我认为这与TypeScript编译JavaScript AOT的所有内容有关,因此运行代码时类型实际上并不存在。但是,我不能通过使用typeMap<string, Type<DockablePanel>
来存储我的类型,因此无法理解它在我的localhost上有效。
为什么这有什么不同?还有另一种方法可以从我忽略的字符串中获取类型吗?
答案 0 :(得分:1)
问题主要与this question有关。
只要开发人员了解后果,就可以使用函数name
。在Node.js等环境中依赖它是相当安全的。如果代码应该缩小(通常在客户端JS中),函数名称也将缩小。
为了确保存储正确的类名并能够恢复它们,应该明确地将类映射到它们的名称(也可以有多个具有相同名称的函数)。
类应具有可识别它们的静态属性。 name
被重新定义是不安全的displayName
is conventional。使名称唯一可能更安全,因为这种方式无法安全存储的类应该导致错误。实现这一点的便捷方法是类装饰器。
另一种方法是拥有已知类名的地图:
const classMap = {
TestPanelComponent,
...
};
存储类时可以引用它。同样,如果地图中不存在类,则应该导致错误。
答案 1 :(得分:0)
我解决了。我在问题中遗漏了一个关键部分:
private typeMap: Map<string, Type<DockablePanel>> = new Map();
constructor(@Inject('panelTypes') private panelTypes: Type<DockablePanel>[]) {
for (let i = 0; i < panelTypes.length; i++) {
this.typeMap.set(panelTypes[i].name, panelTypes[i]);
}
}
这里我使用类型名称作为键将面板的类型放入Map中。解决方案是删除它,只使用panelTypes本身:
constructor(@Inject('panelTypes') private panelTypes: Type<DockablePanel>[]) { }
public setDefaultDockTree(dockTree: DockTreeNode): void {
// more code
const stored = JSON.parse(storageItem, (key, value) => {
if (key === 'type') {
// The changed part:
const panel: Type<DockablePanel> = this.panelTypes.find((panelType: Type<DockablePanel>) => {
return panelType.name === value;
});
if (!panel) {
console.log('Couldn\'t find paneltype ' + value);
} else {
return panel;
}
} else {
return value;
}
});
// More code
}
使用Map不在图片中,我只需使用Type&lt;&gt; .name属性来查找正确的Type。