Storybook 中的 Angular FormGroup:将循环结构转换为 JSON

问题描述 投票:0回答:3

我正在使用角度和故事书。我的模型中有 FormGroup 和 FormArray,但它们不能与故事书一起使用。

a.stories.ts ->

import { CommonModule } from '@angular/common';
import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Meta, moduleMetadata, Story } from '@storybook/angular';

export default {
    title: 'Example/A',
    component: AComponent,
    decorators: [
        moduleMetadata({
            imports: [
                CommonModule,
                ReactiveFormsModule,
            ],
            providers: [],
        }),
    ],
} as Meta;

const Template: Story<AComponent> = (args: AComponent) => ({
    props: args,
});

export const Costs = Template.bind({});
Costs.args = {
    model: {
        questions: [
            {
                ...,
                "question": "lorem ipsum",
                "formGroup": new FormGroup({
                    answers: new FormArray([
                        new FormGroup({
                            "Q020_A001": new FormControl(null, [Validators.required]),
                            "Q020_A002": new FormControl(null, [Validators.required]),
                        }),
                    ]),
                }),
                "answers": [
                    {
                        ...,
                        "answerCode": "Q020_A001",
                    },
                    {
                        ...,
                        "answerCode": "Q020_A002",
                    },
                ],
            }
        ],
    },
};

我在故事书中遇到错误 ->

TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'FormGroup'
    |     property 'controls' -> object with constructor 'Object'
    |     property 'answers' -> object with constructor 'FormArray'
    --- property '_parent' closes the circle
    at JSON.stringify (<anonymous>)

如果“formGroup”为空,它就可以工作。 ->

            "formGroup": new FormGroup({
            }),

但是如果“formGroup”不为空,则不起作用。 ->

        "formGroup": new FormGroup({
            asd: new FormControl(''),
        }),

我该如何解决这个问题?

angular angular-reactive-forms storybook angular-storybook
3个回答
1
投票

我通过创建一个新的 ASstoryComponent 解决了这个问题。

// a-story.component.ts

@Component({
    selector: 'a-component-story',
    template: `<a-component [model]="model"></a-component>`,
})
export class AStoryComponent implements OnInit {

    @Input() model!: any;

    constructor() {}

    ngOnInit() {
        // Converts the "formGroup" property I specified in a.stories.ts to the real formGroup object.
        convertStoryModelToRealModel(this.model);
    }

}


// a.stories.ts

import { Validators } from '@angular/forms';
import { Meta, moduleMetadata, Story } from '@storybook/angular';

export default {
    title: 'Example/A',
    component: AStoryComponent,
    decorators: [
        moduleMetadata({
            declarations: [AStoryComponent, AComponent],
            imports: [
                CommonModule,
                ReactiveFormsModule,
            ],
        }),
    ],
} as Meta;

const Template: Story<AStoryComponent> = (args: AStoryComponent) => ({
    props: args,
});

export const Costs = Template.bind({});
Costs.args = {
    model: {
        questions: [
            {
                ...,
                "question": "lorem ipsum",
                "formGroup": {
                    answers: [
                        {
                            "Q020_A001": [null, [Validators.required]],
                            "Q020_A002": [null, [Validators.required]],
                        },
                    ],
                },
                "answers": [
                    {
                        ...,
                        "answerCode": "Q020_A001",
                    },
                    {
                        ...,
                        "answerCode": "Q020_A002",
                    },
                ],
            }
        ],
    } as any,
};

0
投票

问题在于您向 Storybook 提供的每一个

args
,SB 都会打电话给
JSON.stringify
。这种设计是必需的,因为 SB 也在运行时使用您的
args
并允许您更改值。

在您的情况下,您创建了一个反应式表单模型,这会导致对象无法通过

JSON.stringify
转换为字符串。

要修复它,您需要:

  1. 仅向 SB 提供参数作为原始数据
  2. 更改
    AComponent
    中的逻辑,以根据上面提供的数据创建模型

示例代码:

/// a.stories.ts
Costs.args = {
    model: {
        questions: [
            {
                ...,
                "question": "lorem ipsum",
                "formGroup": {
                    answers: [
                        {
                            "Q020_A001": null,
                            "Q020_A002": null,
                        }),
                    ]),
                },
                "answers": [
                    {
                        ...,
                        "answerCode": "Q020_A001",
                    },
                    {
                        ...,
                        "answerCode": "Q020_A002",
                    },
                ],
            }
        ],
    },
};
/// A.component.ts

export class AComponent {
  @Input() model;

  constructor() {
     // create your reactive forms here with data from model
     this.form = new FormGroup(...)
  }
}

0
投票

我最近找到了一种非常简单的方法来避免此错误,几乎不需要付出任何努力。

Storybook 尝试对您传递给组件的任何参数进行字符串化。我们可以告诉我们的 formGroups 按照我们的规则进行字符串化。

通过在对象上设置 toJSON(),我们可以阻止 Storybook 尝试序列化循环结构。

const MY_FORM = new FormGroup({
    a: new FormControl('A'),
    b: new FormControl('B'),
})

// Tell JSON.stringify to serialize it as 'WHATEVER YOU WANT' > No circular dependency
MY_FORM['toJSON'] = () => 'WHATEVEVER YOU WANT'; 

export const FormStory: Story = {
    name: 'FormStory',
    args: {
        form: MY_FORM
    },
};

Storybook 将在控制面板中生成一个字符串控件,您可以像这样隐藏它(如上所述)

...
argTypes: {
    form: { table: { disable: true } } // Disable form serialization in control panel
},

请注意,这适用于字符串化时循环的任何对象

© www.soinside.com 2019 - 2024. All rights reserved.