KnockoutJS - computed abservable and js object - javascript

I am trying to return a property of an observable but seem to be missing something.
self.SelectedAccountTypeID = ko.computed(function () {
return self.selectedAccountType.AccountTypeID();
});
I am trying to return the AccountTypeID property of selectedAccountType but this is not working
when I try
self.SelectedAccountTypeID = ko.computed(function () {
return self.selectedAccountType();
});
it works but returns a javascript object
Here is a fiddle with the code
http://jsfiddle.net/qafrD/

You are on the right track, because your selectedAccountType is an observable you need to access its value with selectedAccountType()
So the correct syntax: self.selectedAccountType().AccountTypeID;
However because the self.selectedAccountType() can be null you need to check that first before accessing the AccountTypeID on it:
self.SelectedAccountTypeID = ko.computed(function () {
if (self.selectedAccountType())
return self.selectedAccountType().AccountTypeID;
});
Demo Fiddle

This is because self.selectedAccountType is an observable meaning that you need to invoke it like a function to retrieve its current value. The property "AccountTypeID" however is not an observable therefore you do not need parenthesis here.
self.SelectedAccountTypeID = ko.computed(function () {
// Retrieve the value of the observable
var selectedAccountType = self.selectedAccountType();
// The value may be "undefined" or "null" if there has not yet been
// anything stored in the observable
if (selectedAccountType && typeof selectedAccountType.AccountTypeID != "undefined") {
return selectedAccountType.AccountTypeID;
}
// Return a default value otherwise
return null;
});
Demo: http://jsfiddle.net/qafrD/1/

Related

Ember component computed function does not rerun when data changes

I have stored a string value within a computed property like so:
clientId: Ember.computed.oneWay("_clientId")
where _clientId is defined as a property on the object like so:
export default Ember.service.extend { _clientId: null, clientId: Ember.computed.oneWay("_clientId"), updateId() {this.set("_clientId", "fe48822d-bf50-44a1-9ce0-61b06504d726"); } }
I then have a component with a computed property like so:
chartData: Ember.computed("data", function () {
const data = this.get("data");
const clientId = this.get("session").get("clientId");
console.log(clientId);
if (data && data.get("left") !== undefined && data.get("right") !== undefined) {
return data;
}
this.set("_chartDisplayData", null);
return null;
})
When I called updateId, i expected the chartData function to be re-run as the value of the clientId is changed (i verified that the value gets changed for clientId). However, the chartData function never re-runs, why is this?
You need to tell the computed property about all of your dependencies. First, the computed property will never run if it isn't being used somewhere. If you aren't using it you need an observer instead. But assuming you are actually using it, the computed property will only recompute itself when one of the listed dependencies change. And if you list an object as a dependency, it will not update if only some of the object's properties are changed, only if the entire object is replaced. Try this:
chartData: Ember.computed("data.left", "data.right", "session.clientId", function () {
const data = this.get("data");
const clientId = this.get("session.clientId");
console.log(clientId);
if (data && data.get("left") !== undefined && data.get("right") !== undefined) {
return data;
}
this.set("_chartDisplayData", null);
return null;
})

Printing output in console.log for a function

I have a javascript that I am using to pull id of a tag . But due to some limitation on a platform where I need to use this I have to make this script under function so I will get the output in return. I am trying but I am failing to get it.
This is the script I have;
var arr2st3 = [].map.call(document.querySelectorAll('article:nth-child(-n+4)'), function(el) {
return el.id.replace(/[^\d]/g, '');
});
This is what I am trying to come up;
function myfun() {
var arr2st3 = []
var arr2st3 = arr2st3.map.call(document.querySelectorAll('article:nth-child(-n+4)'),
function(el) {
return el.id.replace(/[^\d]/g, '');
});
return;
console.log(myfun);
}
This is the test url -> https://jsfiddle.net/v3d0nz9d/
I need to get output in return as ['123456', '7896669', '1147777']
Any help would be highly appreciated.
TIA
[].map is shorthand for Array.prototype.map, so you don't need to store that in a variable, you can just return the return value of your map. Anything after a return statement won't be called, so your console.log is never reached. Also, to log the values returned from your function, you need to call it in your console.log.
function myfun() {
return [].map.call(document.querySelectorAll('article:nth-child(-n+4)'), function(el) {
return el.id.replace(/[^\d]/g, '');
});
}
console.log(myfun());

Creating custom angular filters

i have a json of items and a barcode scanner, the barcode scanner inputs directly into my app.
Now the items have a primary part number and a secondary part number, more often than not the result from the barcode scanner will be the primary part number, but i have to check with both to be sure.
I'm trying to implement a custom filter to do this, but it doesn't seem to be working, can anyone maybe let me know what i'm doing wrong ?
storeApp.filter('barcodeScanner', function() {
return function(parts, barcode) {
angular.forEach(parts, function (vals, key) {
if( !angular.isUndefined(vals.Part_Number) && vals.Part_Number !== null )
if (angular.equals(vals.Part_Number,barcode))
return parts[key];
});
angular.forEach(parts, function(vals, key) {
if ( !angular.isUndefined(vals.Other_Part_Number) && vals.Other_Part_Number !== null )
if (angular.equals(vals.Other_Part_Number,barcode))
return parts[key];
});
};
});
i then call the filter later in the controller,
$scope.addItemToCart = function() {
$scope.shoppingCart.push($filter('barcodeScanner')($scope.parts, $scope.ItemToAdd));
console.log($scope.Cart.itemToAdd);
console.log($filter('barcodeScanner')($scope.parts, $scope.Cart.itemToAdd));
$scope.Cart.itemToAdd = "";
console.log($scope.shoppingCart);
};
however the result from the filter keeps returning undefined. i know for a fact that the entry i want does exist, because when i use a normal $filter('filter') it works fine, but i cannot risk such a widespread filter for my app.
thanks for any help :)
I believe the problem lies in the forEach part of your function. A forEach function does not return a value. You are returning a value to your iterator function and since the forEach is not returning that returned value from the iterator then you will have nothing to push in your $scope.shoppingCart.push($filter('barcodeScanner')($scope.parts, $scope.ItemToAdd));
Saving to a variable ie. matchedPart declared inside the anonymous factory(wrapper) function and returning it outside of the forEach function should solve the undefined:
storeApp.filter('barcodeScanner', function() {
return function(parts, barcode) {
// declare a variable here
var matchedPart;
angular.forEach(parts, function (vals, key) {
if( !angular.isUndefined(vals.Part_Number) && vals.Part_Number !== null )
if (angular.equals(vals.Part_Number,barcode))
// save it to new variable
matchedPart = parts[key];
});
angular.forEach(parts, function(vals, key) {
if ( !angular.isUndefined(vals.Other_Part_Number) && vals.Other_Part_Number !== null )
if (angular.equals(vals.Other_Part_Number,barcode))
// save it to new variable
matchedPart = parts[key];
});
// return it outside the forEach function
return matchedPart;
};
});
last note:
I would also think you should refactor by combining your forEach functions. Not have 2 separate ones. Combining your isUndefined check with !angular.isUndefined(vals.Part_Number) && !angular.isUndefined(vals.Other_Part_Number) && vals.Part_Number...
Instead of .equals you need to check == because string will not exactly equal to number
angular.equals is nothing but strongly check in javascript === which check both values are equal with their type or not.
if(angular.equals(vals.Other_Part_Number,barcode))
Changed to
if(vals.Other_Part_Number == barcode)
If you want to strictly check then you need to convert both the value to number using parseInt and then check
if(angular.equals(parseInt(vals.Other_Part_Number),parseInt(barcode)))
Hope this could help you. Thanks.

Subscribe arrayChange on knockout computed accessor

I have a case where I need to listen for array changes of a computed that is simply returning a filtered value of an observable.
However, I do need to have the full list of changes, as .subscribe(function(changes){},null,'arrayChange') would do on an observableArray.
What I understand is that arrayChange does not work in the case of a computed value, because it probably remakes a new array and so there's no specific change to list.
See http://jsfiddle.net/darknessm0404/A6D8u/1/ for a complete example.
// The following does not work, but I'd like it
computedTest.subscribe(function(changesList){
console.log('COMPUTED subscription : arrayChange');
}, null, 'arrayChange');
The only way I seem to achieve what I want is to create another observable array which would have push/delete depending on the changes, so I would be able to get the 'arrayChange' method work.
Full example of my idea:
this.events.listFiltered = ko.observableArray().extend({ rateLimit: 0 });
this.events.listFiltered_Worker = ko.computed(function () {
var listFiltered = me.events.listFiltered();
ko.utils.arrayForEach(me.events.list(), function (item) {
index = listFiltered.indexOf(item);
if (FILTERING_CASE_HERE) {
if (index < 0) {
listFiltered.push(item);
}
} else if (index >= 0) { // Delete
listFiltered.splice(index, 1);
}
});
return ko.utils.arrayFilter(me.events.list(), function (item) {
return !(item.end().isBefore(filterStart) || item.start().isAfter(filterEnd));
});
return __rd++;
}).extend({ rateLimit: 0 });
this.events.listFiltered.subscribe(function () {
debug('inside subscribe');
debugger;
}, null, 'arrayChange');
However I was wondering if there's a easier solution to this problem?
Knockout supports arrayChange for any observable, which you have to enable specifically.
var computedTest = ko.computed(function() {
...
}).extend({trackArrayChanges: true});
http://jsfiddle.net/mbest/A6D8u/2/
If you look at the Knockout source code, this is what's done automatically for observable arrays.
If this a common scenario in your project you could create a wrapper function that does this:
function computedArray() {
return ko.computed.apply(ko, arguments).extend({trackArrayChanges: true});
}

Determine if an object property is ko.observable

I'm using KnockoutJS version 2.0.0
If I'm looping through all properties of an object, how can I test whether each property is a ko.observable? Here's what I've tried so far:
var vm = {
prop: ko.observable(''),
arr: ko.observableArray([]),
func: ko.computed(function(){
return this.prop + " computed";
}, vm)
};
for (var key in vm) {
console.log(key,
vm[key].constructor === ko.observable,
vm[key] instanceof ko.observable);
}
But so far everything is false.
Knockout includes a function called ko.isObservable(). You can call it like ko.isObservable(vm[key]).
Update from comment:
Here is a function to determine if something is a computed observable:
ko.isComputed = function (instance) {
if ((instance === null) || (instance === undefined) || (instance.__ko_proto__ === undefined)) return false;
if (instance.__ko_proto__ === ko.dependentObservable) return true;
return ko.isComputed(instance.__ko_proto__); // Walk the prototype chain
};
UPDATE: If you are using KO 2.1+ - then you can use ko.isComputed directly.
Knockout has the following function which I think is what you are looking for:
ko.isObservable(vm[key])
To tack on to RP Niemeyer's answer, if you're simply looking to determine if something is "subscribable" (which is most often the case). Then ko.isSubscribable is also available.
I'm using
ko.utils.unwrapObservable(vm.key)
Update: As of version 2.3.0, ko.unwrap was added as substitute for ko.utils.unwrapObservable

Categories

Resources