Сохранение формы дерева рекурсивно и создание события по окончании - PullRequest
3 голосов
/ 08 мая 2019

У меня есть форма с такими вложенными формами

Form 1
-- Form 1.1
-- Form 1.2

Мне нужно сохранить форму 1, затем сохранить форму 1.1 и форму 1.2, используя Id, полученный из сохранения Form 1, и после этого выдать, что все дочерние элементы сохранены.

Я должен делать это динамически, поскольку глубина этой формы намного больше.

Как лучше выполнить это действие?

Я пытался поместить директиву в теги дочернего компонента, но я не могу прочитать эти компоненты. Эти компоненты разные и могут быть динамическими, поэтому я не могу использовать @ViewChild для компонентов.

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

На данный момент я могу думать только о какой-то службе, которая будет содержать счетчики для каждого родителя.

Обновление # 1

Вот StackBlitz и является упрощенной версией того, что у меня есть. В реальном приложении присутствуют динамический компонент, а также имена динамических параметров (те, которые мне нужно передать от родителя для подключения потомков) и уровень вложенности формы.

Я пытался охватить детали рабочего процесса комментариями.

TL; DR Мне нужно сохранить всех людей, а затем сохранить имена каждого человека, используя personId в качестве дополнительного параметра для подключения их в БД, после того как все части формы сохранены, мне нужно сообщить родительской форме, что все готов.

Ответы [ 3 ]

1 голос
/ 14 мая 2019

У вас есть все, что вам нужно на уровне app.component.ts, чтобы рекурсивно пройти по tree и завершить логику сохранения без какого-либо состояния на дочернем уровне.

Используйте вашу логику Observable в методе submit(), чтобы получить ID из первого сохранения, как только вы получите этот идентификатор, переходите к следующему Выровняйте значения в форме и завершите следующее сохранение. Однажды все ваши условия выполнены на уровне app.component.ts форма.

submit() {
    // Here goes an initial form submit which returns an ID of saved form
    console.log('submit a form');
    let persons: Person[] = this.formGroup.value.persons;
    for (let person of persons) {
      console.log('Saving person with Birthdate: ' + person.birthDate)
      for (let name of person.names) {
        console.log('Saving nameObject: ' + JSON.stringify(name))
      }
    }
}
0 голосов
/ 15 мая 2019

Наконец я решил свою проблему, используя следующий код. Надеюсь, это поможет кому-то

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

0 голосов
/ 08 мая 2019

Используйте отправители событий для отправки данных формы из дочерних форм в родительскую форму. Затем родительская форма сможет собирать отправленные данные, и вы сможете обрабатывать их по своему усмотрению, в том числе передавать их в Observable, как вы упомянули.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...