Ожидание подписки установлено рекурсивно для завершения - PullRequest
1 голос
/ 15 октября 2019

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

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

Вот мой первый проход. Я уверен, что есть гораздо более чистый способ сделать это.

https://codesandbox.io/s/rxjs-playground-hp3wr

// Array structure. Note children.
const navigation = [
  {
    id: "applications",
    title: "Applications",
    children: [
      {
        id: "dashboard",
        title: "Dashboard"
      },
      {
        id: "clients",
        title: "Clients"
      },
      {
        id: "documents",
        title: "Documents",
        children: [
          {
            id: "dashboard",
            title: "Dashboard"
          },...
        ]
      },
      {
        id: "reports",
        title: "Reports"
      },
      {
        id: "resources",
        title: "Resources"
      }
    ]
  }
];

В примере с песочницей кода, глядя на сообщения консоли, я получаю правильный результат,Тем не менее, я бы хотел избежать подписки в setHidden и recursivelySetHidden. Я также хотел бы избежать использования Subject, если это возможно.

1 Ответ

0 голосов
/ 17 октября 2019

Вот мой подход:

const roleObservable = timer(1000).pipe(mapTo("**************"));

function populateWithField(o, field, fieldValue) {
  if (Array.isArray(o)) {
    return from(o).pipe(
      concatMap(c => populateWithField(c, field, fieldValue)),
      toArray()
    );
  }

  if (o.children) {
    return roleObservable.pipe(
      tap(role => (fieldValue = role)),
      concatMap(role => populateWithField(o.children, field, role)),
      map(children => ({
        ...o,
        [field]: fieldValue,
        children
      }))
    );
  }

  return roleObservable.pipe(
    map(role => ({
      [field]: role,
      ...o
    }))
  );
}

of(navigation)
  .pipe(concatMap(o => populateWithField(o, "hidden")))
  .subscribe(console.log, e => console.error(e.message));

Главное, на что стоит обратить внимание, это частое использование concatMap. Это оператор отображения высшего порядка , который означает, среди прочего, что он будет автоматически подписываться на / отписываться от своей внутренней наблюдаемой.

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

В этом случае вам придется иметь дело со множеством Observables-of-Observables (higher-order observables), то есть , поэтому вы должны использовать concatMap каждый раз, когда сталкиваетесь с children собственность. Любой ребенок в этом свойстве может иметь собственное свойство children, поэтому вы должны убедиться, что Observable содержит only first-order Observables.

Вы можете прочитать большео наблюдаемых высшего порядка и первого порядка здесь .

Вот пример CodeSandbox

...