递归保存树形式并在完成时发出事件

时间:2019-05-08 13:58:19

标签: angular typescript recursion tree rxjs

我有一个带有嵌套表格的表格

Form 1
-- Form 1.1
-- Form 1.2

我需要保存表格1,然后使用从保存Id中检索到的Form 1保存表格1.1和表格1.2,然后发出所有子项都被保存的信息。

我必须动态地执行此操作,因为此表单的深度要大得多。

执行此操作的效果如何?

我试图在子组件的标签上放置指令,但是我无法读取这些组件。这些组件是不同的,并且可能是动态的,因此我不能在组件上使用@ViewChild

我想观察这些子组件,以便在告诉父组件一切准备就绪之前知道应该发出多少事件。

就目前而言,我只能想到某些服务,其中包含每个父母的计数。

更新#1

这里是StackBlitz,是我所拥有的简化版本。在实际的应用程序中,存在动态组件以及动态参数名称(我必须从父级传递来连接子级的名称)和表单的嵌套级别。

我尝试用注释覆盖工作流程的详细信息。

TL; DR 我需要保存所有Person,然后使用personId作为附加参数保存每个Person的名称以在数据库中连接它们,在保存完所有表单部分后,我需要报告父表单,一切准备就绪。

3 个答案:

答案 0 :(得分:1)

您拥有app.component.ts级别所需的一切,可以递归遍历tree并完成您的保存逻辑,而在子级别没有任何状态。

  

Observable方法中使用submit()逻辑来检索ID   从第一个保存开始,一旦收到该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))
      }
    }
}

答案 1 :(得分:0)

使用事件发射器将子窗体中的窗体数据提交到父窗体中。然后,父表单将能够收集发出的数据,并且您可以根据需要对其进行处理,包括将其传递给您提到的Observable。

答案 2 :(得分:0)

最后,我用下一个代码解决了我的问题。希望这会帮助某人

// 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

完成后,我只调用对象上的saveRecursively并保存了所有内容。

此外,如果您要使用我的方法并在OnInit中扩展对象,请不要忘记删除OnDestroy中的值,以便将每个FormArray拆分为每个控件不会有任何删除的值。