Наконец я решил свою проблему, используя следующий код. Надеюсь, это поможет кому-то
// You should provide either "data" or "formGroup"
export interface RecForm {
/** Data to be saved */
data?: any;
/** Request URI */
request: string;
/** Params that should be used from accumulator */
useParams?: string[] | {
/** Parameter to be taken from accumulator */
use: string;
/** Parameter alias to use */
as: string;
}[];
/** Children forms to be saved */
children?: RecForm[];
/** FormGroup to take values from */
formGroup?: FormGroup;
/** Children forms grouped by name to be saved */
childrenGroups?: {
[index: string]: RecForm[];
};
/** Names to use for storing in accumulator. The array us being used for the
* sake of agility */
valAlias: string[];
}
И этот метод я использую для обработки этого объекта
public saveRecursively(obj: RecForm, accumulator?: any): Observable<any> {
// Variable to store form data
let req;
if (obj.data) {
req = {...obj.data};
} else {
req = {...obj.formGroup.value};
}
// If there are an accumulator and parameters to be used
// we add these parameters to the form data
if (obj.useParams && accumulator) {
(<any>obj.useParams).forEach(paramName => {
if (typeof paramName === 'string') {
req[paramName] = accumulator[paramName];
} else {
req[paramName.as] = accumulator[paramName.use];
}
});
}
return iif(
() =>
// We need to save only changed form
obj.formGroup.touched && obj.formGroup.dirty
// or the one that has children and does not
// have an Id to pass it to the children
|| (
obj.children && obj.children.length > 0
|| obj.childrenGroups && Object.keys(obj.childrenGroups).length > 0
) && !req.id
,
defer(() => this.ss.send(obj.request, {
data: req
})),
of(req.id || null)
).pipe(
pluck('data'),
map((id: string) => {
const acc = {...accumulator};
// Export returned ID with provided aliases adding them
// to the accumulator
obj.valAlias.forEach(alias => {
acc[alias] = id;
});
return acc;
}),
switchMap((acc): any => {
if (obj.children && obj.children.length > 0) {
return forkJoin(
obj.children.map(child => this.saveRecursively(child, acc))
);
}
// Save children forms if there any
else if (obj.childrenGroups && Object.keys(obj.childrenGroups).length > 0) {
return forkJoin(
flatten(Object.values(obj.childrenGroups)).map(item => this.saveRecursively(item, acc))
);
}
// If there is no children forms just return an accumulator
else {
return of(acc);
}
})
);
}
Примечание: flatten
взято из Lodash и используется для изменения и размещения сгруппированных форм по имени в одном массиве.
this.ss.save
- это метод службы, который принимает запрос и тело для обработки и возвращает наблюдаемое.
Затем я просто создаю сервис, экземпляр которого создается в корневом компоненте формы.
Этот сервис содержит object: RecForm
и несколько методов Lodash для динамического создания и удаления объектов вложенных объектов.
Имея этот сервис, я прохожу через дерево компонентов path
, где расширяю его значение, перемещаясь по дереву, и использую этот путь для добавления новых FormGroup
s к моему объекту.
Когда я закончу, я просто позвоню saveRecursively
на объекте и сохраню все как положено.
Также, если вы собираетесь использовать мой метод и расширить объект в OnInit
, не забудьте удалить его значение в OnDestroy
, чтобы любые FormArray
, разделенные на компоненты каждым элементом управления, не удалялись значение.