Как объект JSON аргумента ipcRenderer теряет данные при передаче из сервиса Angular 7? - PullRequest
0 голосов
/ 21 апреля 2019

У меня странная проблема с проектами Angular 7.1.1 и Electron 4.1.4.

Поток данных:

  1. Angular Component "ОтчетBuilder »собирает параметры конфигурации отчета из проверенной формы FormGroup и FormControl и отправляет данные в docx-templater.service
    • Пользовательская кнопка запускает функцию createReport ()
    • При отправке параметров для полного отчета,Функция createReport () вызывает функцию dataService fnGetCompleteControlList (), которая асинхронно возвращает правильно сконфигурированный JSON.
    • с функцией .then () после асинхронного извлечения данных, функция createReport () объединяет выходной каталог, который является частью конфигурацииформирует и отправляет оба в функцию createCompleteDocument () docx-templater.service.Как только обещание возвращается, оно обновляет пользовательский интерфейс.
  2. Функция Angular Service "docx-templater" createCompleteDocument передает данные и значения папок в ipcRenderer.send для электрона "writeCompleteDocument"channel и возвращает обещание.
  3. В моем main.ts у меня есть ipcMain.on для канала "writeCompleteDocument", который передает данные в функцию write-docx для обработки этих данных в текстовый документ.

Проблема: Когда данные попадают в мою функцию write-docx, они пропускают подмассив объектов, которые необходимы для процесса экспорта.

У меня естьпроверил, что данные идеальны в консоли Chrome Developer Tools в электронном виде в тот момент, перед тем, как он отправляет данные в docx-templater.service, и непосредственно перед тем, как этот сервис отправляет их в ipcRenderer (это означает, что моя служба данных и функции построителя отчетовработает как задумано).Когда я проверяю данные в файле main.ts, сохраняя их в файле JSON, в нем отсутствует подмассив Controls только во втором объекте JSON.Подмассив элементов управления отображается в первом объекте, как и ожидалось.

Я отмечу, что то, что выходит из функции 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.по функции, но как-то это обрезка части данных.Я даже пытался отфильтровать данные перед отправкой их в средство визуализации, а затем проанализировать их на другой стороне, но это ничего не дало.

Может ли это быть асинхронным?Я не знаю, куда идти дальше, чтобы отладить и найти, какую идиотскую ошибку я допустил в процессе.

Кроме того, я понимаю, что приведенный выше поток данных кажется слишком сложным для того, что я делаю, и что явозможно, это проще сделать, но это имеет смысл (вроде) для структуры всего приложения, поэтому я собираюсь пойти на это, если смогу устранить эту ошибку.

Ответы [ 2 ]

0 голосов
/ 22 апреля 2019

Похоже, что ваша функция 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 документы , следующее, вероятно, делает то, что вы хотите:

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

Похоже, что обратный вызов прошел Объект события .

Другой вариант - просто заменить ipcRenderer#send() на ipcRenderer#sendSync() в исходном коде, но, как указано в документации этого метода:

Отправка синхронного сообщения заблокирует весь процесс рендеринга, если вы не знаете, что делаете, никогда не должны его использовать.

Использование ipcRenderer#send() и ipcRenderer#once() - это почти наверняка путь.

Отдельно вы можете очистить код, переключившись на функции асинхронного / ожидания . Например:

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
  }
}
0 голосов
/ 21 апреля 2019

Я смог решить эту проблему, добавив тайм-аут на 1000 мс после того, как мой fnGetCompleteControlList () извлек данные в report-builder.component.ts. Кажется, у меня гораздо больше работы с изучением асинхронных функций. :-(

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
    }
...