0

将 Angular 升级到最新版本和 stroybook 后,我遇到了一个以前运行良好的组件的问题。

TS 文件:

const defaultConfig = {
    h1: "test",
    h2: "test",
    message: "test",
    hasBackButton: false,
    submitButtonLabel: "test",
    cancelButtonLabel: "test",
    width: 550,
} as const;

export default {
    title: "Test component",
    component: TestComponent,
    decorators: [
        moduleMetadata({
            imports: [ConfirmationModalModule, HttpClientModule],
        })
    ],
    args: defaultConfirmationModalArgs
} as Meta;

export const Properties: Story<typeof defaultConfirmationModalArgs> = (args) => ({
    props: {
        config: {
            h1: args.h1,
            h2: args.h2,
            message: args.message,
            hasBackButton: args.hasBackButton,
            submitButtonLabel: args.submitButtonLabel,
            cancelButtonLabel: args.cancelButtonLabel,
            width: args.width,
    }
});

html:

<modal-header
    [h1]="config.h1"
    [h2]="config.h2"
    [hasBackButton]="config.hasBackButton"
></modal-header>
<modal-body>
</modal-body>
<modal-footer>
</modal-footer>

之前运行良好的 html 文件,现在出现错误,我必须将组件包装在*ngIf="config"可以帮助我吗? 在此处输入图像描述

4

1 回答 1

0

从 Angular 12 升级到 Angular 13 后,我身边也出现了同样的问题。我认为当您尝试绑定对象时总是会发生这种情况。我对storybook的代码做了进一步的分析,发现主要问题是storybook在AfterViewInit的StorybookWrapperComponent中设置了绑定。但是我不明白为什么这在以前的 Angular 版本中不是问题,因为如果你考虑整个生命周期,同样的问题应该已经在以前的版本中发生了。更新的 Angular 版本是否在生命周期的行为方面实现了任何新的功能?对无效值可能更严格吗?

Storybook Wrapper 组件(将呈现组件)的代码如下(如您所见, OnInit 未绑定值,AfterViewInit 绑定实际值):

StorybookWrapperComponent.prototype.ngOnInit = function () {
        var _this = this;
        // Subscribes to the observable storyProps$ to keep these properties up to date
        this.storyWrapperPropsSubscription = this.storyProps$.subscribe(function (storyProps) {
            if (storyProps === void 0) { storyProps = {}; }
            // All props are added as component properties
            Object.assign(_this, storyProps); //  --> Here the properties are assigned, but not to the actual component which is rendered
            _this.changeDetectorRef.detectChanges(); // --> Here we will trigger the rendering which then creates the issue
            _this.changeDetectorRef.markForCheck();
        });
    };
    StorybookWrapperComponent.prototype.ngAfterViewInit = function () {
        var _this = this;
        // Bind properties to component, if the story have component
        if (this.storyComponentElementRef) {
            var ngComponentInputsOutputs_1 = NgComponentAnalyzer_1.getComponentInputsOutputs(storyComponent);
            var initialOtherProps = getNonInputsOutputsProps(ngComponentInputsOutputs_1, initialProps);
            // Initializes properties that are not Inputs | Outputs
            // Allows story props to override local component properties
            initialOtherProps.forEach(function (p) {
                _this.storyComponentElementRef[p] = initialProps[p];
            });
            // `markForCheck` the component in case this uses changeDetection: OnPush
            // And then forces the `detectChanges`
            this.storyComponentViewContainerRef.injector.get(core_1.ChangeDetectorRef).markForCheck();
            this.changeDetectorRef.detectChanges();
            // Once target component has been initialized, the storyProps$ observable keeps target component inputs up to date
            this.storyComponentPropsSubscription = this.storyProps$
                .pipe(operators_1.skip(1), operators_1.map(function (props) {
                // removes component output in props
                var outputsKeyToRemove = ngComponentInputsOutputs_1.outputs.map(function (o) { return o.templateName; });
                return Object.entries(props).reduce(function (prev, _a) {
                    var _b;
                    var key = _a[0], value = _a[1];
                    return (__assign(__assign({}, prev), (!outputsKeyToRemove.includes(key) && (_b = {},
                        _b[key] = value,
                        _b))));
                }, {});
            }), operators_1.map(function (props) {
                // In case a component uses an input with `bindingPropertyName` (ex: @Input('name'))
                // find the value of the local propName in the component Inputs
                // otherwise use the input key
                return Object.entries(props).reduce(function (prev, _a) {
                    var _b, _c;
                    var propKey = _a[0], value = _a[1];
                    var input = ngComponentInputsOutputs_1.inputs.find(function (o) { return o.templateName === propKey; });
                    return __assign(__assign({}, prev), (input ? (_b = {}, _b[input.propName] = value, _b) : (_c = {}, _c[propKey] = value, _c)));
                }, {});
            }))
                .subscribe(function (props) {
                // Replace inputs with new ones from props
                Object.assign(_this.storyComponentElementRef, props);
                // `markForCheck` the component in case this uses changeDetection: OnPush
                // And then forces the `detectChanges`
                _this.storyComponentViewContainerRef.injector.get(core_1.ChangeDetectorRef).markForCheck();
                _this.changeDetectorRef.detectChanges();
            });
        }

更新 从 6.4.15 升级到 6.4.17 后,这个问题似乎在最新版本的故事书中得到解决,一切正常。

于 2022-02-01T13:35:30.190 回答