从Angular 7服务传递时,ipcRenderer参数JSON对象如何丢失数据?

时间:2019-04-21 13:03:22

标签: json angular electron ipcrenderer

我在Angular 7.1.1和Electron 4.1.4项目中遇到了一个奇怪的问题。

数据流:

  1. Angular组件“ Report Builder”从FormGroup和FormControl验证的表单中收集报告配置选项,并将数据发送到docx-templater.service
    • 用户按钮触发createReport()函数
    • 提交完整报告的选项时,createReport()函数将调用dataService的fnGetCompleteControlList(),该异步返回正确配置的JSON。
    • 在异步数据检索之后,使用.then()函数,createReport()函数将输出目录合并到配置表单中,并将两者发送到docx-templater.service的createCompleteDocument()函数。返回承诺后,它将更新UI。
  2. Angular Service“ docx-templater”的createCompleteDocument函数将数据和文件夹值传递给电子“ writeCompleteDocument”通道的ipcRenderer.send,并返回一个Promise。
  3. 在main.ts中,我有一个用于“ writeCompleteDocument”通道的ipcMain.on,该通道将数据传递到write-docx函数,以将该数据处理为word文档。

问题: 当数据到达我的write-docx函数时,它缺少导出过程必不可少的对象子数组。

在将数据发送到docx-templater.service之前,以及在该服务将其发送到ipcRenderer之前(我的数据服务),我已经验证了该数据在电子的Chrome开发人员工具控制台中是否完美和报表生成器功能正在按设计工作)。当我通过将数据保存到JSON文件中来检查main.ts中的数据时,它仅缺少JSON的第二个对象内的controls子数组。控件子数组按预期显示在第一个对象中。

我将注意到ipcMain函数产生的是一个格式正确的JSON文件,因此它实际上只是排除了“ controls”子数组,并且不会由于内存或缓冲区限制或类似原因而被截断。

report-builder.component.ts

createReport() {
    if (this.reportBuilderFG.get('allControls').value) {
      this.db.fnGetCompleteControlList()
        .then((groups: Group[]) => {
          this.word.createCompleteDocument(groups, this.reportBuilderFG.get('folder').value + '\\filename.docx')
          .then(() => {
            this.openSnackBar(this.reportBuilderFG.get('folder').value + '\\filename.docx created successfully');
          });
        });
    } else {
      // Do other stuff
    }
docx-templater.service.ts

createCompleteDocument(data, folder: string): Promise<boolean> {
    return new Promise(resolve => {
      console.log(data) <=== Data is perfect here.
      ipcRenderer.send('writeCompleteDocument', {data: data, folder: folder});
      resolve();
    });
  }

main.ts
import { writeCompleteDocument } from './node_scripts/write-docx';

ipcMain.on('writeCompleteDocument', (event, arg) => {
  fs.writeFileSync("IPCdata.json", arg.data); // <==== Part of the data is missing here.
  writeCompleteDocument(arg.data, arg.folder);
});

Good Data Example (some keys and objects excluded for brevity)
[
  {
    "name": "General Security",
    "order": 1,
    "subgroups": [
      {
        "_id": "GOV",
        "name": "Governance",
        "order": 1,
        "controls": [
          {
            "group": "GS",
            "subgroup": "GOV",
            "active": true,
            "printOrder": 1,
            "name": "This is my GS control name",
            "requirements": [
              {
                "id": "SA01",
                "active": true,
                "order": 1,
                "type": "SA",
                "applicability": [
                  "ABC",
                  "DEF",
                  "GHI"
                ],
              },
              { ... 3 more  }
            ],
            "_id": "GSRA-03",
            "_rev": "1-0cbdefc93e56683bc98bae3a122f9783"
          },
          { ... 3 more }
    ],
    "_id": "GS",
    "_rev": "1-b94d1651589eefd5ef0a52360dac6f9d"
  },
  {
    "order": 2,
    "name": "IT Security",
    "subgroups": [
      {
        "_id": "PLCY",
        "order": 1,
        "name": "Policies",
        "controls": [ <==== This entire sub array is missing when exporting from IPC Main
          {
            "group": "IT",
            "subgroup": "PLCY",
            "active": true,
            "printOrder": 1,
            "name": "This is my IT control name",
            "requirements": [
              {
                "id": "SA01",
                "active": true,
                "order": 1,
                "type": "SA",
                "applicability": [
                  "ABC",
                  "DEF",
                  "GHI"
                ],
              }
            ],
            "_id": "GSRA-03",
            "_rev": "1-0cbdefc93e56683bc98bae3a122f9783"
          }
      }
    ],
    "_id": "IT",
    "_rev": "2-e6ff53456e85b45d9bafd791652a945c"
  }
]

我希望ipcRenderer完全将JSON传递给ipcMain.on函数,但是不知何故,它会修剪部分数据。在将数据发送到渲染器之前,我什至尝试对数据进行分层处理,然后在另一侧对其进行解析,但这无济于事。

这可能是异步的吗?我不知道下一步该去哪里调试,并且发现我在此过程中犯了什么白痴错误。

此外,我意识到上述数据流对于我正在做的事情来说似乎过于复杂,并且我可能可以更轻松地完成此工作,但这对于整个应用程序的结构方式来说是有道理的,因此我将如果我能解决这个问题,那就去吧。

2 个答案:

答案 0 :(得分:0)

我能够通过在Report-builder.component.ts中的fnGetCompleteControlList()数据提取之后添加1000毫秒的超时来解决此问题。似乎我还有很多工作要学习异步功能。 :-(

report-builder.component.ts

createReport() {
    if (this.reportBuilderFG.get('allControls').value) {
      this.db.fnGetCompleteControlList()
        .then((groups: Group[]) => {
          setTimeout(() => {
              this.word.createCompleteDocument(groups, this.reportBuilderFG.get('folder').value + '\\filename.docx')
              .then(() => {
                  this.openSnackBar(this.reportBuilderFG.get('folder').value + '\\filename.docx created successfully');
              });
          }, 1000);
        });
    } else {
      // Do other stuff
    }

答案 1 :(得分:0)

好像您的createCompleteDocument()函数设置不正确。快速搜索显示ipcRenderer是一个异步函数,但是您(几乎)同步对其进行响应。

您有以下内容,(可能)是不正确的(实际上绝对是不正确的,因为您在Promise<boolean>处键入了Promise<void>的返回值):

createCompleteDocument(data, folder: string): Promise<boolean> {
  return new Promise(resolve => {
    ipcRenderer.send('writeCompleteDocument', {data: data, folder: folder});
    resolve();
  });
}

ipcRenderer#send()是异步的,但是您随后立即调用resolve(),而无需等待函数解析。这可能可以解释为什么添加setTimeout()可以为您解决问题。查看ipcRenderer docs,以下内容可能会满足您的要求:

createCompleteDocument(data, folder: string): Promise<Event> {
  return new Promise(resolve => {
    ipcRenderer.once('writeCompleteDocument', resolve);
    ipcRenderer.send('writeCompleteDocument', {data: data, folder: folder});
  });
}

好像回调函数传递了一个Event object

另一种选择是在原始代码中简单地将ipcRenderer#send()替换为ipcRenderer#sendSync(),但是该方法的文档中指出了这一点:

  

发送同步消息将阻止整个渲染器进程,除非您知道自己在做什么,否则永远不要使用它。

几乎肯定要使用ipcRenderer#send()ipcRenderer#once()

您可以分别切换到async/await functions来清理代码。例如:

async createReport(): Promise<void> {
  if (this.reportBuilderFG.get('allControls').value) {
    const groups: Group[] = await this.db.fnGetCompleteControlList();

    await this.word.createCompleteDocument(
      groups,
      this.reportBuilderFG.get('folder').value + '\\filename.docx'
    );

    // Unclear if this function is actually async 
    await this.openSnackBar(
      this.reportBuilderFG.get('folder').value +
        '\\filename.docx created successfully'
    );
  } else {
    // Do other stuff
  }
}