【问题标题】:items not getting correctly added to observable array项目未正确添加到可观察数组
【发布时间】:2017-01-12 22:53:58
【问题描述】:

我正在从 JSON 数据创建一个可观察的数组。要么我最初创建的视图模型错误,要么我错误地将新数据添加到数组中。当我添加到数组后 console.log out 时,新添加的数据是不同的。 这是截图: 如您所见,brocCode=1800 之后的对象与数组中的所有先前项不同。我认为问题在于,当我在表格中添加新行时,我正在创建一个新的 itemModel 并将其添加到项目列表中。当我获取 JSON 数据并将其设为可观察数组时,我不会创建 itemModels 的可观察数组。

这是我的KnockoutJS:(具体看itemsModel中的self.addLine函数和self.items = ko.observableArray(items);):

var itemModel = function (data) {
        var self = this;
        self.invalidItem = ko.observable(true);
        self.itemNo = ko.observable(data ? data.itemNo : '').extend( {
            required: {
                params: true,
                message: "Item no. required."
            }
        });
        self.brocCode = ko.observable(data ? data.brocCode : '').extend( {
            required: {
                params: true,
                message: "Bro code required."
            }
        });
        self.itemDesc = ko.observable(data ? data.itemDesc : '').extend( {
            required: {
                params: true,
                message: "Item desc required."
            }
        });
        self.retail = ko.observable(data ? data.retail : '').extend( {
            required: {
                params: true,
                message: "Retail required."
            }
        })
        .extend({numeric: 2});
        self.prizeNum = ko.observable(data ? data.prizeNum : '').extend( {
            required: {
                params: true,
                message: "Prize num required."
            }
        });
        self.itemOrder = ko.observable(data ? data.itemOrder : '').extend( {
            required: {
                params: true,
                message: "Item order required."
            }
        });
    }

    var itemsModel = function(items) {
        var self = this;
        self.items = ko.observableArray(items);
        //self.items = ko.mapping.fromJSON(items, itemModel);

        self.invalidItem = ko.observable(true);

        self.checkItemNo = function(data) {
            //console.log("lost focus - " + self.invalidItem());
            var itemNo = $.trim(data.itemNo());
            console.log(itemNo);
            if (itemNo != "") {
                var item = "";
                $.each(window.listOfItems, function(i, v) {
                    if (v.No.search(itemNo) != -1) {
                        item = v.Description;
                        return;
                    }
                });
                console.log(item);
                if(item != "") {
                    console.log(self.items());
                    var match = ko.utils.arrayFirst(self.items(), function(newItem) {
                        console.log("checking " + newItem.itemNo);
                        return itemNo === newItem.itemNo;
                    });
                    console.log("match: " + match);
                    if (!match) {
                        data.itemDesc(item);
                    } else { // item already entered
                        data.invalidItem(true);
                        setTimeout(function() { data.invalidItem(true); }, 1);
                        data.itemDesc("");
                        slideDownMsg("Item already entered.");
                        slideUpMsg(3000);
                    }
                } else { // invalid item #
                    console.log(data);
                    data.invalidItem(true);
                    setTimeout(function() { data.invalidItem(true); }, 1);
                    data.itemDesc("");
                    slideDownMsg("Invalid item number.");
                    slideUpMsg(3000);
                }
            }
        }

        self.submit = function() {
            //self.showErrors(true);
            if (viewModel.errors().length === 0) {
                console.log('Thank you.');
                $("#brochureForm").submit();
            }
            else {
                console.log('Please check your submission.');
                viewModel.errors.showAllMessages();
                $(".input-validation-error").first().focus();
            }
        }

        self.addLine = function() {
            var iModel = new itemModel();
            iModel.invalidItem(true);
            self.invalidItem(true);
            console.log("adding new line; it is: " + self.invalidItem());
            self.items.push( iModel );
            //setTimeout(function() { self.invalidItem(true); }, 1);
        };

        self.insertLine = function(index) {
            self.items.splice(index, 0, new itemModel() );
        };

        self.removeItem = function(item) {
            self.items.remove(item);
        };

        self.errors = ko.validation.group(self.items, { deep: true, live: true });

        self.validate = function() {
            self.errors.showAllMessages();
        }
    };

    var profitCode = function(code, desc, name) {
        this.code = code;
        this.desc = desc;
        this.name = name;
    };

    var codeModel = function(codes) {
        var self = this;
        self.availableProfitCodes = ko.observableArray([])
        self.codes = ko.observableArray(codes);
    }

    var profitItemsModel = function(items) {
        var self = this;
        self.items = ko.observableArray(items);
    }

    var combined = (function () {
        function combinedVM() {
            this.codes = ko.observable(codeModel);
            this.items = ko.observable(profitItemsModel);
            this.availableProfitCodes = codeModel.availableProfitCodes;
        }
        return combinedVM;
    })();

    ko.bindingHandlers.enterPress = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var allBindings = allBindingsAccessor();
            element.addEventListener('keydown', function (event) {
                var keyCode = (event.which ? event.which : event.keyCode);
                if (keyCode === 13 || (!event.shiftKey && keyCode === 9)) {
                    event.preventDefault();
                    //bindingContext.$root.invalidItem(false);
                    bindingContext.$root.addLine();
                    return false;
                }
                return true;
            });
        }
    };

    ko.bindingHandlers.insertPress = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var allBindings = allBindingsAccessor();
            element.addEventListener('keydown', function (event) {
                var keyCode = (event.which ? event.which : event.keyCode);
                if (keyCode === 45) {
                    event.preventDefault();
                    bindingContext.$root.insertLine(ko.unwrap(valueAccessor()));
                    return false;
                }
                return true;
            });
        }
    };

    ko.bindingHandlers.selected = {
        update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            var selected = ko.utils.unwrapObservable(valueAccessor());
            if (selected) element.select();
        }
    };        

    function GetItems() {
        var itemsJSON = @Html.Raw(Json.Encode(Model.brochureItems));
        viewModel = new itemsModel(itemsJSON);
        ko.applyBindings(viewModel, $("#itemListContainer")[0]);
    }

GetItems() 在 Document.ready 上被调用。

【问题讨论】:

  • 您需要使用ko.mapping.fromJS() 才能将现有itemsJSON 中的所有属性转换为ko.observable()。试试var itemsJSON = ko.mapping.fromJS(@Html.Raw(...))(可能需要安装映射插件:knockoutjs.com/documentation/plugins-mapping.html)
  • 没有骰子,Error: The argument passed when initializing an observable array must be an array, or null, or undefined.
  • 我想我需要以某种方式将其转换为 itemModel 数组
  • 映射到模型数组:ko.observableArray(items.map(function(data) { return new itemModel(data) }));
  • @user3297291,这行得通!谢谢!如果您愿意,可以添加为答案。现在我只需要弄清楚为什么我的匹配(arrayFirst)函数不起作用。但我会为此发布另一个问题。

标签: knockout.js


【解决方案1】:

您的itemsJSON 是一个普通对象数组。尽管它们可能看起来像itemModel(它们共享相同的属性名称),但有很大的不同:它们没有具有可观察的属性值,它们的值也没有由您的扩展程序验证。

在您的addLine 方法中,您通过调用new itemModel() 正确地实例化了一个新的itemModel。因此,您的可观察数组将包含普通对象和 itemModel 实例的混合。这不一定是个问题,但如果您希望能够轻松处理数组的值,最好确保它们都共享相同的类型。

您可以通过遍历itemsJSON 中的对象将初始数据映射到正确的视图模型:

var itemModels = itemsJSON.map(function(data) {
  return new itemModel(data);
});

我建议您遵循命名约定并将 itemModel 重命名为 ItemModel。如果它适合您的风格,您可以创建一个静态帮助方法,为您返回一个新实例:

ItemModel.create = function(data) { return new ItemModel(data); };

var itemModels = itemsJSON.map(ItemModel.create);

【讨论】:

    猜你喜欢
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-06
    相关资源
    最近更新 更多