I built a dockable panels solution in which I want to store the layout to localStorage
, so I can use it again when I reload the page. This is actually working like a charm on my localhost. These are the most important models and how I use them:
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
);
The code for saving and restoring my layout is as follows:
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;
}
Now in local storage my saved dockTree looks like this:
{
"left": {
"type": "TestPanelComponent",
},
"right": {
"type": "SomePanelComponent",
},
// More
}
But when I commit it to the svn repository and Jenkins builds with ng build --prod
and starts a Docker container running the build, the saved value for type will be complete nonsense:
{
"left": {
"type": "",
},
"right": {
"type": "n",
}
// More stuff
}
I figured this would have to do with TypeScript compiling everything to JavaScript AOT, so the types don't actually exist when running the code. However, I don't understand that it does work on my localhost by simply using the typeMap<string, Type<DockablePanel>
to store my types.
Why is this any different? Is there another way to get a type from a string that I am overlooking?