在生产中保存和恢复localStorage的类型

时间:2018-02-16 09:46:45

标签: angular typescript local-storage

我构建了一个可停靠的面板解决方案,我想将布局存储到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上有效。

为什么这有什么不同?还有另一种方法可以从我忽略的字符串中获取类型吗?

2 个答案:

答案 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。