Knockout computed observable dont eval when subscribed - javascript

I am creating a ko.computedObservable like so:
var holder = ko.observable();
var makeAjax = function(){
holder('new value');
};
var result = ko.computed({
read: function () {
makeAjax();
return holder();
},
write: function (newValue) {
//not relevant
},
deferEvaluation: true
});
I would like to be able to subscribe to result without causing the read function to execute.
result.subscribe(function(val){
console.log(val);
});
However this call to subscribe causes read to execute. Is there any way to subscribe without causing it to execute?

What version of Knockout are you using? I can't re-create this behavior. Here is a snippet I ran on JSbin with KO 3.0 and running it does not cause the result read function to evaluate until you call getResult()
var vm = function() {
var holder = ko.observable();
var makeAjax = function() {
holder('new value');
};
var result = ko.computed({
read: function() {
console.log('excuting read')
makeAjax();
return holder();
},
write: function(newValue) {
console.log('setting holder to ' + newValue)
//not relevant
holder(newValue)
},
deferEvaluation: true
});
result.subscribe(function(val) {
console.log(val);
});
this.getResult = function() {
return result;
}
this.setResult = function(val) {
result(val);
}
}
var VM = new vm();
ko.applyBindings(VM);
VM.setResult("test");

Related

Javascript & knockoutjs: how to refactor the following code to be able to access the properties outside the function

Im struggling to find a way to get the properties Override & Justification available outside of the function. The code is:
self.CasOverridesViewModel = ko.observable(self.CasOverridesViewModel);
var hasOverrides = typeof self.CasOverridesViewModel === typeof(Function);
if (hasOverrides) {
self.setupOverrides = function() {
var extendViewModel = function(obj, extend) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
extend(obj[property]);
}
}
};
extendViewModel(self.CasOverridesViewModel(), function(item) {
item.isOverrideFilledIn = ko.computed( function() {
var result = false;
if (!!item.Override()) {
result = true;
}
return result;
});
if (item) {
item.isJustificationMissing = ko.computed(function() {
var override = item.Override();
var result = false;
if (!!override) {
result = !item.hasAtleastNineWords();
}
return result;
});
item.hasAtleastNineWords = ko.computed(function() {
var justification = item.Justification(),
moreThanNineWords = false;
if (justification != null) {
moreThanNineWords = justification.trim().split(/\s+/).length > 9;
}
return moreThanNineWords;
});
item.isValid = ko.computed(function() {
return (!item.isJustificationMissing());
});
}
});
}();
}
I've tried it by setting up a global variable like:
var item;
or
var obj;
if(hasOverrides) {...
So the thing that gets me the most that im not able to grasp how the connection is made
between the underlying model CasOverridesviewModel. As i assumed that self.CasOverridesViewModel.Override() would be able to fetch the data that is written on the screen.
Another try i did was var override = ko.observable(self.CasOverridesViewModel.Override()), which led to js typeError as you cannot read from an undefined object.
So if anyone is able to give me some guidance on how to get the fields from an input field available outside of this function. It would be deeply appreciated.
If I need to clarify some aspects do not hesitate to ask.
The upmost gratitude!
not sure how far outside you wanted to go with your variable but if you just define your global var at root level but only add to it at the moment your inner variable gets a value, you won't get the error of setting undefined.
var root = {
override: ko.observable()
};
root.override.subscribe((val) => console.log(val));
var ViewModel = function () {
var self = this;
self.override = ko.observable();
self.override.subscribe((val) => root.override(val));
self.load = function () {
self.override(true);
};
self.load();
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

AngularJS promise return value from inside a service seems to change

I am using a service to make a series of API-Calls whenever I poll new data:
angular.module('dashboardApp')
.service('data', function($http, $q) {
var _this = this;
_this.server = function() {
/*get server address*/
};
_this.polling = function() {
_this.myData = {};
console.log('_this.myData emptied');
}
_this.getData = function(){
var deferred = $q.defer();
_this.server().then(function(serverconf) {
/*...*/
deferred.resolve();
};
var getDataA = function(){
/*---*/
_this.myData = {a : 'a'};
};
var getDataB = function(){/*...*/};
var getDataC = function(){/*...*/};
var promiseA = deferred.promise.then(getDataA);
var promiseB = deferred.promise.then(getDataB);
var promise = $q.all([pomiseA,pomiseB]).then(function(resp){
/* do something here */
}).then(getDataC).then(function(resp2){
/*...*/
_this.myData = {c : 'c'};
return _this.myData;
});
return promise;
};
return {
getData = _this.getData,
polling = _this.polling,
};
};
I am changing my data throughout my API-calls in the service. And I can see that the data changes inside the controller making the call everytime an API-Call was successfull.
var polling = function() {
data.getData().then(function(myData) {
$scope.myDataset = myData;
setTimeout(function() {
data.polling();
polling();
}, 3000);
});
};
$scope.$watch('myDataSet', function(newVal, oldVal) {
console.debug(newVal);
}, true);
//output:
//{}
//{a:'a'}
//{b:'b'}
I deactivated everything else, so I am pretty sure the new data is coming from the service itself.
Edit: Forgot some important stuff when I simplified the code.
Thanks to the help of NewDev, I think the problem is that I return a $http-promise in each of my promises.
But since I had not enough time to dig deeper, I worked out a quick work-around which fixed my problem:
var polling = function() {
data.getData().then(function(myData) {
$scope.returned = true;
$scope.myDataSet = myData;
setTimeout(function() {
data.polling();
polling();
}, 3000);
});
};
$scope.$watch('myDataSet', function(newVal, oldVal) {
if($scope.returned === true){
$scope.myDataSet = newVal;
$scope.returned = false;
}else{
$scope.myDataSet = oldVal;
}
}, true);
This way I am monitoring if the new data was returned after all promises have been resolved and control the update this way.

Create a function / event for user in Javascript prototype

I have a Protoype in my project as follows:
(function($) {
$.fn.observerPages = function(pageNo) {
return new ObserverPage($(this), pageNo);
};
function ObserverPage(Parent, pageNo) {
try {
this.parent = Parent;
this.isInitialized = false;
this.timer = -1;
this.highChart = [];
} catch (e) {
}
}
ObserverPage.prototype = {
initialize: function(url) {
isInitialized = true;
},
update: function(controllerurl) {
if (isInitialized == true) {
//Update
}
},
startTimer: function(url, interval) {},
stopTimer: function() {}
};
})(jQuery);
This initiates a page that is hooked with an observer and gets data from an ajax service at a defined interval.
What I require is to put an event in this where the user can put a function when this.isInitialized becomes true.
Currently I am using it like this.
var page = $("liveDiv").observerPages(2);
page.initialize("http://localhost:5549/GetLatestNews");
page.update("http://localhost:5549/GetLatestNewsData");
I want an event that the user can handle when isInitialized gets true and can be used like this
page.onInitialize(function(){
//User writes his function
});
How can I do that?
Solution might be similar to this:
var event = new Event('pageinit');
ObserverPage.prototype = {
initialize: function(url) {
isInitialized = true;
},
update: function(controllerurl) {
if (isInitialized == true) {
document.dispatchEvent(event);
}
},
startTimer: function(url, interval) {},
stopTimer: function() {},
onInitialize: function(callback)
{
document.addEventListener('pageinit', callback, false);
}
};
EDIT: this->document

Access local function inside ko.computed

How come this line of code doesnt work.
Im using durandal/knockout and i have a structure like this
define(function () {
var vm = function() {
compute: ko.computed(function() {
return _compute(1); // fail
});
var _compute= function(e) {
return e;
}
}
return vm;
});
Basically I am just trying to access the private method _compute - but KO.compute doesnt allow that?
Even if i make it public, I still cant access it.
I trying to implement revealing pattern in this, but still no luck!
var vm = function() {
compute: ko.computed(function() {
return this._compute(1); // still failing
});
this._compute= function(e) {
return e;
}
}
update: so far, only this one works
define(function () {
var vm = function() {
var self = this;
var self._compute= function(e) {
return e;
}
compute: ko.computed(function() {
return this._compute(1); // works
}, self);
}
but like I said, _compute is not meant to be exposed.
Update: actually its another error.
this one now works
define(function () {
var vm = function() {
var self = this;
var _compute= function(e) {
return e;
}
compute: ko.computed(function() {
return _compute(1); // works
});
}
Basically, just need to declare the private function before the ko.computed prop!
Thanks!
Additional Note:
Why does it need to be declared before the computed function? I prefer all my "properties" in the first lines while the functions in the bottom. It is neater i Think.
This syntax does not create a property when in a function:
compute: ko.computed(function() {
return _compute(1); // fail
});
You have to use = instead of :.
Try this
var vm = function() {
var self = this;
var _compute = function(e) {
return e;
}
this.compute = ko.computed(function() {
return _compute(1);
});
}
Also note that this is not how you should use a computed observable. It should contain calls to other observables!
From doc:
What if you’ve got an observable for firstName, and another for
lastName, and you want to display the full name? That’s where computed
observables come in - these are functions that are dependent on one or
more other observables, and will automatically update whenever any of
these dependencies change.

Clean and Rebind Data using KnockoutJs Template

So I bind my Knockout template as follows:
First ajax, get data then I pass the data can call a function named bindKo:
function bindKo(data) {
var length = data.length;
var insertRecord = {};
if (length > 0) {
insertRecord = data[data.length - 1]; //last record is an empty PremlimViewModel for insert
insertRecord.Add = true;
data.splice(data.length - 1, 1); //remove that blank insert record
}
function prelims(data) {
var self = this;
var model = ko.mapping.fromJS(data, { copy: ["_destroy"] }, self);
self.BidPriceFormatted = ko.computed({
read: function () {
var bidPrice = this.BidPrice();
if (bidPrice) {
if (!isNaN(bidPrice)) {
var input = '<input type="text" value="' + bidPrice + '"/>';
return $(input).currency({ decimals: 0 }).val();
}
}
},
write: function (value) {
value = value.replace(/\D/g, '');
this.BidPrice(value);
},
owner: this
});
return model;
}
var mapping = {
create: function (options) {
return new prelims(options.data);
}
};
function viewModel(prelimData) {
var self = this;
self.prelims = ko.mapping.fromJS(prelimData, mapping);
self.remove = function (prelim) {
self.prelims.destroy(prelim);
};
self.addOption = function () {
var clone = jQuery.extend(true, {}, insertRecord);
self.prelims.push(ko.mapping.fromJS(clone));
};
}
ViewModel = new viewModel(data);
ko.applyBindings(ViewModel);
}
I have a template defined where you can add and remove records, and user does just that:
<script type="text/html" id="PrelimsTemplate">
<!--Template Goodness-->
</script>
Then, ajax call, records updated in datanbase, latest results returned and I do:
ko.mapping.fromJS(newestData, ViewModel)
But this does not work because my ViewModel is complex.
So I would just like to reBind the template entirely. Make is disappear and reappear with latest data.
Wrap your template in a container than you can hook onto with jQuery.
When you need to trash it use ko.cleanNode and jQuery .empty()
emptyTemplate: function(){
ko.cleanNode($('#template-container')[0]);
$('#template-container').empty();
}
Load your template back up
fillTemplate: function(){
$('#template-container').html('<div data-bind="template: {name:\'templateId\', data: $data}"></div>');
ko.applyBindings(data,$('#template-container')[0])
},
See my fiddle

Categories