Reputation: 1392
I am creating an observable array from JSON data. Either I am creating the viewmodel wrong initially or I am adding new data to the array incorrectly. When I console.log out the array after adding to it, the newly added data is different.
Here is a screenshot:
As you can see the object after
brocCode=1800
is different than all the previous items in the array. I think the issue is that when I am adding a new line to the table, I am creating a new itemModel
and adding that to the items list. When I am getting the JSON data and making that an observable array, I am not making an observable array of itemModel
s.
Here is my KnockoutJS: (specifically look at the self.addLine function and self.items = ko.observableArray(items);
in itemsModel
):
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()
gets called on Document.ready.
Upvotes: 2
Views: 158
Reputation: 23397
Your itemsJSON
is an array of plain objects. Even though they might look like an itemModel
(they share the same property names), there's a big difference: they do not have observable property values, nor are their values validated by your extensions.
In your addLine
method, you correctly instantiate a new itemModel
by calling new itemModel()
. Your observable array will therefore contain a mix of plain objects and itemModel
instances. This doesn't have to be a problem, but if you want to be able to easily work with the array's values, it's better to make sure they all share the same type.
You can map your initial data to the correct viewmodels by iterating over the objects in itemsJSON
:
var itemModels = itemsJSON.map(function(data) {
return new itemModel(data);
});
I'd advice you to follow naming conventions and rename itemModel
to ItemModel
. If it suits your style, you can create a static helper method that returns a new instance for you:
ItemModel.create = function(data) { return new ItemModel(data); };
var itemModels = itemsJSON.map(ItemModel.create);
Upvotes: 1