javascript - 在 observabel 数组中添加的元素不可观察 + ReactJs

标签 javascript reactjs mobx react-dom mobx-react

我有一个可观察的数组,它工作正常。我有添加按钮,用于在向该数组添加/推送元素后向该数组添加元素,添加的元素不可观察,但仍有其他元素可观察。 我正在使用 mobx-react。 这是代码片段:

    import * as React from "react";
import {computed, observable} from "mobx";
import {observer} from "mobx-react";
import {AreaBody} from "../General/AreaBody";
import {AppStore} from "../Stores/AppStore";
import {Strings} from "../Stores/StringsStore";
import {DialogStore, DialogResult} from "../Stores/DialogStore";
import {NotificationStore} from "../Stores/NotificationStore";
import {AddButton} from "../Buttons/AddButton";
import {SaveButton} from "../Buttons/SaveButton";
import {DeleteButton} from "../Buttons/DeleteButton";
import {CloseButton} from "../Buttons/CloseButton";
import {Toolbar} from "../General/Toolbar";
import {InputBooleanComponent} from "../Inputs/InputBooleanComponent";
import {InputNumberComponent} from "../Inputs/InputNumberComponent";
import {InputChoiseComponent} from "../Inputs/InputChoiseComponent";
import {InputTextComponent} from "../Inputs/InputTextComponent";
import * as MetaManagement from "../MetaManagement";
import * as Utilities from "../Utilities";
import {Urls} from "../Urls";
import * as UiStore from "../States/AreaUiInfo";

interface IMetaProps extends UiStore.IAreaUiInfo {
    params: IMetaPropsParams;
}

interface IMetaPropsParams {
    id: string;
}

@observer
export class MetaEdit extends React.Component<IMetaProps, {}> {
    @observable model = new MetaManagement.MetaDetails();
    @observable hash: string;
    @observable isBusy: boolean = true;
    @observable canSaveData: boolean = false;

    componentDidMount() {
        window.addEventListener('beforeunload', this.keepOnPage);
        this.getModel();
        this.canSaveData = this.canSaveModel();
    }

    componentWillUnmount() {
        window.removeEventListener('beforeunload', this.keepOnPage);
    }


    keepOnPage = (e: any) => {
        if (this.canSaveModel()) {
            DialogStore.show("do you want to reload this site?", 'Warning!\n\nNavigating away from this page will delete your text if you haven\'t already saved it.').then((result: DialogResult) => {
                if (result == DialogResult.Yes) {
                    return 'Warning!\n\nNavigating away from this page will delete your text if you haven\'t already saved it.';
                }
            });
        }
    }

    componentDidUpdate(prevProps: IMetaProps) {
        if (this.props.params.id != this.model.guid) {
            this.getModel();
            this.canSaveData = this.canSaveModel();
        }
    }

    getModel() {
        this.isBusy = true;
        MetaManagement.getMetaAsync(this.props.params.id).then(model => {
            this.model = this.fillMissing(model);
            this.hash = JSON.stringify(this.model);
            this.forceUpdate();
            NotificationStore.clearMessage();
            this.isBusy = false;
        }).catch(error => {
            NotificationStore.showErrorMessage(error);
        });
    }

    //@computed get canSave(): boolean {
    //  for (let i = 0; i < this.model.metaValues.length; i++) {
    //      if (!this.model.metaValues[i].value.isValid || !this.model.metaValues[i].value.value) {
    //          return false;
    //      }
    //  }
    //  return this.model.id.isValid && this.model.caption.isValid && (this.hash != JSON.stringify(this.model));
    //}

    canSaveModel = () => {
        for (let i = 0; i < this.model.metaValues.length; i++) {
            if (!this.model.metaValues[i].value.isValid || !this.model.metaValues[i].value.value) {
                return false;
            }
        }
        return this.model.id.isValid && this.model.caption.isValid && (this.hash != JSON.stringify(this.model));
    }

    handleSave = () => {
        if (this.canSaveModel()) {
            let updateModel = new MetaManagement.MetaDetailsUpdate();
            updateModel.lastModified = this.model.lastModified;
            updateModel.captionLastModified = this.model.captionLastModified;
            updateModel.guid = this.model.guid;
            updateModel.dataType = this.model.dataType.value;
            updateModel.id = this.model.id.value;
            updateModel.caption = this.model.caption.value;
            updateModel.required = this.model.required.value;
            updateModel.showAsColumn = this.model.showAsColumn.value;
            updateModel.showAsFilter = this.model.showAsFilter.value;
            updateModel.filterMaxValues = this.model.filterMaxValues.value;
            updateModel.regex = this.model.regex.value;
            updateModel.multiple = this.model.multiple.value;
            updateModel.languageDependent = this.model.languageDependent.value;
            updateModel.predefined = this.model.predefined.value;
            if (updateModel.predefined) {
                updateModel.metaValues = this.model.metaValues;
            }
            else {
                updateModel.metaValues = [];
            }

            MetaManagement.insertOrUpdateMetaAsync(updateModel).then(model => {
                this.model = this.fillMissing(model);
                if (this.model.isValid) {
                    this.hash = JSON.stringify(this.model);
                    NotificationStore.showSuccessMessage(Strings.general.saveSuccessMessage);
                }
                else {
                    NotificationStore.showErrorMessage(model.validationMessage);
                }
            }).catch(error => {
                NotificationStore.showErrorMessage(error);
            });
        }
        this.canSaveData = this.canSaveModel();
    }

    handleDelete = () => {
        DialogStore.show(Utilities.format(Strings.general.dialogDeleteTitle, this.model.caption.value), Utilities.format(Strings.general.dialogDeleteText, this.model.caption.value)).then(result => {
            if (result == DialogResult.Yes) {
                MetaManagement.removeMetaAsync({ guid: this.model.guid, lastModified: null }).then(status => {
                    NotificationStore.showSuccessMessage(Strings.general.deleteSuccessMessage);
                    AppStore.history.replace(Urls.metas);
                }).catch(error => {
                    NotificationStore.showErrorMessage(error);
                });
            }
        });
    }

    handleCancel = () => {
        AppStore.history.replace(Urls.metas);
    }

    handleChange = () => {
        this.canSaveData = this.canSaveModel();
        NotificationStore.clearMessage();
    }

    @computed get showRegex(): boolean {
        return this.model.dataType.value == "Text" || this.model.dataType.value == "Hierarchy";
    }

    @computed get showLanguageDependent(): boolean {
        return this.model.dataType.value == "Text" || this.model.dataType.value == "Hierarchy";
    }

    @computed get showMultiple(): boolean {
        return this.model.dataType.value != "Boolean";
    }

    @computed get showPredifined(): boolean {
        return this.model.dataType.value == "Text" || this.model.dataType.value == "Number" || this.model.dataType.value == "Hierarchy" || this.model.dataType.value == "DocumentLink";
    }

    fillMissing(model: MetaManagement.MetaDetails) {
        model.dataType.label = Strings.metaManagement.metaEdit.dataType;
        model.id.label = Strings.metaManagement.metaEdit.id;
        model.id.placeHolder = Strings.metaManagement.metaEdit.idPlaceHolder;
        model.caption.label = Strings.metaManagement.metaEdit.caption;
        model.caption.placeHolder = Strings.metaManagement.metaEdit.captionPlaceHolder;
        model.regex.label = Strings.metaManagement.metaEdit.regex;
        model.regex.placeHolder = Strings.metaManagement.metaEdit.regexPlaceHolder;
        model.predefined.label = Strings.metaManagement.metaEdit.preDefined;
        model.multiple.label = Strings.metaManagement.metaEdit.multiple;
        model.required.label = Strings.metaManagement.metaEdit.required;
        model.languageDependent.label = Strings.metaManagement.metaEdit.languageDependent;
        model.showAsColumn.label = Strings.metaManagement.metaEdit.showAsColumn;
        model.showAsFilter.label = Strings.metaManagement.metaEdit.showAsFilter;
        model.filterMaxValues.label = Strings.metaManagement.metaEdit.filterMaxValues;
        model.filterMaxValues.placeHolder = Strings.metaManagement.metaEdit.filterMaxValuesPlaceHolder;
        for (let i = 0; i < model.metaValues.length; i++) {
            model.metaValues[i].value.regex = "^[a-zA-Z0-9- _]+$";
        }
        return model;
    }

    render() {
        let regex = this.showRegex ? <InputTextComponent model={this.model.regex} onChange={this.handleChange} /> : null;
        let languageDependent = this.showLanguageDependent ? <InputBooleanComponent model={this.model.languageDependent} onChange={this.handleChange} /> : null;
        let multiple = this.showMultiple ? <InputBooleanComponent model={this.model.multiple} onChange={this.handleChange} /> : null;
        let predefined = this.showPredifined ? <InputBooleanComponent model={this.model.predefined} onChange={this.handleChange} /> : null;
        let values = this.showPredifined && this.model.predefined.value ? <div><h2>{Strings.metaManagement.metaEdit.preDefinedValues}</h2><MetaValuesEdit handleChange={this.handleChange} values={this.model.metaValues} readonly={this.model.predefined.isReadOnly} number={this.model.dataType.value == "Number"} /></div> : null;

        return <AreaBody loading={this.isBusy} areaUiInfo={this.props.areaUiInfo}>
            <Toolbar>
                <SaveButton onClick={this.handleSave} disabled={!this.canSaveData} label={Strings.general.saveButtonLabel}/>
                <DeleteButton onClick={this.handleDelete} label={Strings.general.deleteButtonLabel}/>
                <span className="buttongroup is--buttongroup-right">
                    <CloseButton onClick={this.handleCancel} />
                </span>
            </Toolbar>
            <div className="area-grid__body-content">
                <div className="content-main">
                    <h1>{Strings.metaManagement.metaEdit.title}</h1>
                    <div>
                        <InputChoiseComponent model={this.model.dataType} onChange={this.handleChange} />
                        <InputTextComponent model={this.model.id} onChange={this.handleChange} />
                        <InputTextComponent model={this.model.caption} onChange={this.handleChange} />
                        <InputBooleanComponent model={this.model.required} onChange={this.handleChange} />
                        <InputBooleanComponent model={this.model.showAsColumn} onChange={this.handleChange} />
                        <InputBooleanComponent model={this.model.showAsFilter} onChange={this.handleChange} />
                        <InputNumberComponent model={this.model.filterMaxValues} locale={navigator.language} onChange={this.handleChange}/>
                        {regex}
                        {multiple}
                        {languageDependent}
                        {predefined}
                        {values}
                    </div>
                </div>
            </div>
        </AreaBody>;
    }
}

interface IMetaValuesProps {
    values: MetaManagement.MetaValue[];
    readonly: boolean;
    number: boolean;
    handleChange: () => void;
}

@observer
class MetaValuesEdit extends React.Component<IMetaValuesProps, {}> {


    handleAdd = () => {
        let newValue = new MetaManagement.MetaValue();
        newValue.guid = Utilities.newGuid();
        newValue.canDelete = true;
        newValue.value.isReadOnly = false;
        newValue.value.isValid = true;
        newValue.value.value = "";
        newValue.value.regex = "^[a-zA-Z0-9- _]+$";
        this.props.values.push(newValue);
        this.props.handleChange();
    }

    handleDelete = (item: MetaManagement.MetaValue) => {
        for (let i = 0; i < this.props.values.length; i++) {
            if (this.props.values[i].guid == item.guid) {
                this.props.values.splice(i, 1);
                this.props.handleChange();
                return;
            }
        }
    }

    handleChange = () => {
        this.props.handleChange();
    }

    render() {
        return <div className="list-object is--tools-single">
            <div className="toolbar">
                <span className="buttongroup is--buttongroup-right">
                    <AddButton onClick={this.handleAdd} disabled={this.props.readonly} />
                </span>
            </div>
            <ul>
                {
                    this.props.values.map(item => {
                        return <li key={item.guid}>
                            <div>
                                <div className="list-object__tools">
                                    <div className="tinytool">
                                        <DeleteButton onClick={() => this.handleDelete(item) } disabled={!item.canDelete} />
                                    </div>
                                </div>
                                <div className="list-object__name">
                                    <InputTextComponent onChange={this.handleChange} model={item.value} />
                                </div>
                            </div>
                        </li>
                    })
                }
            </ul>
        </div>;
    }
}

this.model.metaValues 是一个可观察数组。

请提出一些想法。

最佳答案

您无法concat可观察数组。尝试推送新元素:

this.model.metaValues.push(newValue);

关于javascript - 在 observabel 数组中添加的元素不可观察 + ReactJs,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41627307/

相关文章:

javascript - 从 2 个不同的文件读取为二维数组 Javascript

javascript - 如何判断字符串是否是code-method?

javascript - MobX - 检索可观察数组对象的索引?

reactjs - 如何使类对象在 MobX 中可观察?

javascript - 如何根据第三个数组从另一个数组创建一个新数组?

javascript - ReactJS -> 从 promise 存储变量

javascript - 我已经使用 webpack 生成了bundle.js,但我的 React 应用程序继续使用/static/bundle.js

javascript - 如何在React中的功能组件中制作与componentDidUpdate函数类似的函数?

javascript - 如何重写这个 promise 链以避免需要多个函数?

JavaScript 单元测试或端到端测试