Вот мой подход:
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