knockout variable not defined - scoping issue? - javascript

I have the following code:
var ObjectViewModel = function (testObject) {
//debugger;
var self = this;
self.id = testSet.id;
self.details = testOject.details;
self.children = ko.observableArray(testObject.children);
self.childCount = ko.computed(function() {
return self.children().length;
});
self.addObject = function () {
//debugger;
// Pending UI
// Call API here
// On success, complete
self.children.push(dummyObject);
self.childToAdd("");
}.bind(self);
}
/ etc
However in childCount, this.children() is undefined. I'm trying to let the view show the length of the children array in real-time, so as the user adds/removes items, the count is updated. Any idea why this isn't working?

You can pass what the value of this should be when the function is executed to the computed function with the last parameter:
this.childCount = ko.computed(function() {
return this.children().length;
}, this);
You could also store a reference to this outside of the computed:
var self = this;
this.childCount = ko.computed(function () {
return self.children().length;
});

Related

Knockout computed function called "too late"...?

I need a knockout computed function to execute directly, but it seems more like knockout queues computables and executes when it feels like it. is there a way (apart from not using knockout) to get immediate execution?
I have stepped through my code and seen that my routine continues past the call to the computed function and later I stop at a breakpoint in the function - when it's too late.
I have removed 95% of the code but the variable names are still there and they might look a bit odd in this context...
var self = this;
self.temp = true;
self.UsePatterns = ko.observable(false);
self.UsePatterns.subscribe(function () {
self.ShowReport();
});
self.PatternColors = ko.computed(function () {
var retValue = self.UsePatterns() ? true : false
return retValue;
});
self.ShowReport = function () {
self.temp = self.PatternColors();
alert (self.temp);
};
self.UsePatterns(true);
self.UsePatterns(false);
self.UsePatterns(true);
https://jsfiddle.net/tommypeters/1ubfa4ev/22/
One would think this should get true, false, true and not the reverse...
You should use ko.pureComputed() instead.
var self = this;
self.temp = true;
self.UsePatterns = ko.observable(false);
self.UsePatterns.subscribe(function (nv) {
self.ShowReport();
});
self.PatternColors = ko.pureComputed(function () {
var retValue = self.UsePatterns() ? true : false
return retValue;
});
self.ShowReport = function () {
self.temp = self.PatternColors();
console.log('Show report:',self.temp);
};
self.UsePatterns(true);
self.UsePatterns(false);
self.UsePatterns(true);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

How to bind array data from Json to knockout observable array

Please find below code from .cshtml page
Below code gives databind for observables but not for observable array.
var getLocationDetails = function (locationCode)
{
return $.getJSON('#Url.Action("MasterDetails")',
{ locationcode: locationCode });
};
var setModelData = function (self, locationDetails)
{
self.UneditedLocationDetails = _(locationDetails).cloneDeep();
for (var key in locationDetails)
{
locationDetails[key] = ko.observable(locationDetails[key]);
}
function LocationModifyViewModel()
{
var self = this;
initAjax(self);
var initModel = setModelData.bind(self);
var initModel = setModelData.bind(self);
self.TextboxLocationcode.subscribe(function (val)
{
getLocationDetails(val).success(function (locationDetails) {
setModelData(self, locationDetails);
});
In the above code databinded to locationDetails, where I want to add observablearray values to locationDetails or else if possible to new variable.

KnockoutJS passing ko.computed values to a function

I have a working knockout js script, myValues is an array result of a ko.computed, then I pass it down to myValues2 which calls a myFunction to make each object in that array as "observable". This works but I wanted to call myFunction in self.myValues so that I wouldn't have to create another observableArray myValues2. Can you help me combine the script of myValues and myValues2 so that I can finally delete self.myValues2. Here is my code:
var myFunction = function (id, name, amount, automatic) {
var self = this;
self.Id = ko.observable(id);
self.Name = ko.observable(name);
self.Amount = ko.observable(amount);
self.Automatic = ko.observable(automatic);
};
self.myValues = ko.computed(function () {
return ko.utils.arrayFilter(self.completeList(), function (ded) {
return ded.automatic() == true;
});
});
self.myValues2 = ko.observableArray();
self.myValues2(ko.utils.arrayMap(self.myValues(), function (dd) {
return new myFunction(dd.id(), dd.name(), dd.amount(), dd.automatic());
}));
self.completeList()
how defined this array?
Because you began to filter this list by automatic field and then to fill other list. Strange order.
return ded.automatic() == true;

KnockoutJS - ViewModel Grandparent - Parent - Child using ko.computed to access Parent/Grandparent Value

I have a Grandparent, Parent, Child ViewModel relationship setup in knockout and knockout mapping, CustomerViewModel, WorkOrderViewModel, and RepairViewModel.
I want to setup a child ko.computed value within the child that Take the amount of hours in the RepairViewModel and multiplies it by Rate within the WorkOrderView Model.
Within the RepairViewModel I have code like this:
self.RepairCost = ko.computed(function () {
return (self.Hours() * self.parent.LabourChargeCost()).toFixed(2);
});
Is there any way to get the parent's value?
Thanks so much!
Here is the JS code I'm using (simplified):
var workOrderMapping = {
'WorkOrders': {
key: function (workOrders) {
return ko.utils.unwrapObservable(workOrders.WorkOrderId);
},
create: function (options) {
return new WorkOrderViewModel(options.data);
}
},
'Repairs': {
key: function (repairs) {
return ko.utils.unwrapObservable(repairs.RepairId);
},
create: function (options) {
return new RepairViewModel(options.data);
}
}
};
RepairViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, workOrderMapping, self);
self.RepairCost = ko.computed(function () {
return (self.Hours() * self.parent.LabourChargeCost()).toFixed(2);
})
;
}
WorkOrderViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, workOrderMapping, self);
}
CustomerViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, workOrderMapping, self);
self.save = function () {
//alert(ko.toJSON(self));
$.ajax({
url: "/Customers/Save/",
type: "POST",
data: ko.toJSON(self),
contentType: "application/json",
success: function (data) {
//alert("succ");
//alert(data.customerViewModel);
// if (data.customerViewModel != null) {
//alert("succ2");
new PNotify({
title: 'Saved',
text: 'Record saved successfully',
type: 'success',
styling: 'bootstrap3'
});
ko.mapping.fromJS(data.customerViewModel, workOrderMapping, self);
if (data.newLocation != null)
window.location = data.newLocation;
},
});
};
}
You can't access the parent in the child model unless you pass the parent to the child and kept a reference.
What has worked better for me in the past is passing the value to the child model and then add to subscribe in the parent to update the child when the value is changed.
function Parent(){
var self = this;
self.someValue = ko.obserable();//init if you need to
self.children = [new Child(self.someValue())]
self.someValue.subscribe(function(value){
for(var i = 0;i<self.children.length;i++){
self.children[i].parentValue(value);
}
});
}
function Child(value){
var self = this;
self.parentValue = ko.observable(value);
}
Unfortunately you cannot do that, and it is a limitation not of Knockout but JS: you don't have access to the parent context from within an object property.
What you can do is, as #GrayTower mentioned, pass your parent as a parameter, but this to me, feels a bit like a hack (I admit to using it sometimes though). You could also modify your child viewmodels once they have been initiated, from either within the parent, or externally, before the view model is bound. I don't really understand the flow of properties in the code you presented, but I hope a smaller test case will suit your needs: http://jsfiddle.net/kevinvanlierde/507k237y/. Suppose we have the following parent-child viewmodels:
// symbolizes an employee
function ChildVM(name, hoursWorked) {
var self = this, parent;
this.name = name;
this.hoursWorked = hoursWorked;
}
// symbolizes a payment system
function MasterVM() {
var self = this;
this.rate = 25; // dollars/hour
this.employees = [
new ChildVM('Chris',16),
new ChildVM('Cagle',32)
];
}
var app = new MasterVM(),
view = document.getElementsByTagName('table')[0];
We want to add a property payout to each ChildVM in employees, which will use rate from MasterVM in combination with hoursWorked from ChildVM, i.e. a ko.computed. You could simply paste a function inside the MasterVM constructor, like this:
ko.utils.arrayForEach(self.employees, function(i) {
i.payout = ko.computed(function() {
return i.hoursWorked*self.rate;
});
});
Or you could make it a method and call it before calling ko.applyBindings:
this.initEmployees = function() {
ko.utils.arrayForEach(self.employees, function(i) {
i.payout = ko.computed(function() {
return i.hoursWorked*self.rate;
});
});
}
app.initEmployees();
ko.applyBindings(app, view);
Or you could even build an applyBindings wrapper, which executes a 'callBefore' (cf. <=> AJAX callbacks) before binding the view and the model, like so:
function initVM(VM, callbefore, element) {
callbefore(VM);
ko.applyBindings(VM, element);
}
initVM(app, function(vm) {
ko.utils.arrayForEach(vm.employees, function(i) {
i.payout = ko.computed(function() {
return i.hoursWorked*vm.rate;
});
});
},view);
fiddle
Note: Using ko.mapping.fromJSconverts all values to observables, while when your values don't need updating you don't need observables, you can use plain JS values/ objects.

KnockoutJs computed array not calculated correctly

The code is as follows: EmployeeModel is the viewModel and the problem is that when I change an item's property - deletedFlag in employees (obs array), deletedItems is not updated.
How can i fix this?
function Employee(data) {
this.employeid = ko.observable(data.employeid);
this.name = ko.observable(data.name);
this.isactive = ko.observable(data.isactive);
this.deletedFlag = ko.observable(false);
}
var EmployeeModel = function () {
var self = this;
self.employees = ko.observableArray([]);
self.deletedItems = ko.computed(function () {
return ko.utils.arrayFilter(self.employees(), function (item) {
return item.deletedFlag == true;
});
}, this);
}
EDIT: and the following code marks one item from the array for deletion
self.removeEmployee = function (employee) {
employee.deletedFlag(true);
};
The property deletedFlag is an observable, therefore you need to retrieve its current value by invoking it as a function (you cannot compare it directly to any value):
self.deletedItems = ko.computed(function () {
return ko.utils.arrayFilter(self.employees(), function (item) {
return item.deletedFlag() == true; // <===
});
}, this);

Categories

Resources