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

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>

Related

How to execute nested Javascript function

I am troubleshooting a slider problem at the moment, however, I don't know javascript that well, I have isolated the .js file that is responsible for the slider functioning, there is a destroy function that I would like to fire off, the code looks like this
(function ($) {
$.pixelentity = $.pixelentity || {version: '1.0.0'};
$.pixelentity.peBackgroundSlider = {
conf: {
api: false,
wait: false
},
paused: false
};
function PeBackgroundSlider(target, conf) {
...
function destroy() {
prevColor = currentColor = currentBW = jwindow = jthis = undefined;
target.data("peBackgroundSlider", null);
target = undefined;
}
}
How would I fire off the destroy function in this scenario?
You can't as it is right now.
To call it you must "export" it as follows:
function PeBackgroundSlider(target, conf) {
...
function destroy() {
prevColor = currentColor = currentBW = jwindow = jthis = undefined;
target.data("peBackgroundSlider", null);
target = undefined;
}
return { "destroy": destroy };
}
From the caller:
var ret = PeBackgroundSlider();
Now you can do:
ret.destroy();
Or, more concise:
return destroy;
And:
innerDestroy = PeBackgroundSlider();
And finally:
innerDestroy();

Intercept a function in javascript

Following is my javaScript code.
var myObjfn = {
before : function(){
console.log("before");
},
loadA: function(){
console.log("loadA");
},
loadB: function(){
console.log("loadB");
},
loadC: function(){
console.log("loadC");
}
}
Whenever I call myObjfn.loadA(), it should call myObjfn.before() method before executing loadA method. Same for loadB() & loadC(). I don't want to explicitly call before() method in all loadA,loadB and loadC methods. Is there any option to achive this in javascript ?
You could do something like this. Which creates a wrapper function for each function in the object except the before function.
var myObjfn = { ... };
Object.keys(myObjfn).forEach(key => {
if (key === "before") return;
var oldFunc = myObjfn[key];
myObjfn[key] = function() {
myObjfn.before();
return oldFunc.apply(this, arguments);
};
});
myObjfn.loadA();
// "before"
// "loadA"

Send event when module was executed

I'm really stuck on this.. I need to send an event when both Load module and Hide module code was executed, and only then send the event. Ideas on how to achieve this?
// Load module
(
function() {
var s=document.createElement('script');
s.type='text/javascript';
s.async=true;
s.src='https://example.com/bundles.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
)();
// Hide module
var inverval = setInterval(hideClass, 100);
function hideClass () {
if ($(".class").hide().length > 0) clearInterval(inverval);
}
// When both happend = Send a event to Google Analytics
DigitalData.push({'event':Module, 'eventLabel':'Page'});
If this is your only option, then perhaps there's something you are going about wrongly. Anyway, let's see ... Only when both events have taken place.
var HandleTwoEvents = function (key1, key2) {
this.count = 0;
this.pack = [];
$self = this;
this.startListening = function(fn) {
fn = fn || function () {}
window.addEventListener(key1, function (ev) {
if ($self.pack.indexOf(key1) < 0) {
$self.pack.push(key1);
$self.count++;
if ($self.count == 2) {
fn();
$self.count = 0;
}
}
console.log(key1, ev);
});
window.addEventListener(key2, function (ev) {
if ($self.pack.indexOf(key2) < 0) {
$self.pack.push(key2);
$self.count++;
if ($self.count == 2) {
fn();
$self.count = 0;
}
}
console.log(key2, ev);
});
}
}
Forgive me, i always use this function to create events
function createEvent(name, obj) {
var evt = document.createEvent("Event");
evt.initEvent(name, true, true);
evt.data = obj;
dispatchEvent(evt);
}
Now, to log both events ...
var both = new HandleTwoEvents("EventKeyOne", "EventKeyTwo");
both.startListening(function () {console.log("This means that both Events have taken place")});
Now, let's test ...
createEvent("EventKeyOne", {});
//key, data are the arguments ... function defined in startListening above does not execute, and upon inspection, both.count is seen to be 1
createEvent("EventKeyTwo", {});
//Now, function executes.
//It also works if "EventKeyTwo" is raised before "EventKeyOne"
Happy Coding!
PS: I'm sure there's a better way to handle the use of the $self variable, with some function binding, i guess. I've never been able to learn it.

Knockout.Js Array filter syntax

Just getting into javascript and knockout.js. I've found a bunch of examples of what I'm trying to accomplish. And I feel like there is a small syntax error I may be overlooking. I'm trying to filter a set already returned (this.tasks) from a server via ajax/json. I have that working fine. What I would like to do, is have users be able to switch between complete and incomplete tasks.
I switched the code to just run the foreach loop on tasksFiltered. "this.done" is either true or false.
Task template
var taskModel = function(id, title, description, done){
var self = this;
this.id = ko.observable(id);
this.title = ko.observable(title);
this.description = ko.observable(description);
this.done = ko.observable(done);
this.showEdit = ko.observable(false);
this.titleUpdate = ko.observable(false);
this.descriptionUpdate = ko.observable(false);
};
Page Model
var pageModelTasks = function(){
var self = this;
this.task_title = ko.observable("");
this.task_description = ko.observable("");
this.task_title_focus = ko.observable(true);
this.tasks = ko.observableArray([]);
this.tasksFiltered = ko.computed(function() {
return ko.utils.arrayFilter(this.tasks, function(Task) {
return Task.done == true;
});
});
// CRUD functions excluded
};
this doesn't work.
Two minor corrections to your code. First, as #XGreen mentioned, you need to pass the array value, not the observableArray instance, as the first parameter of the arrayFilter function. Lastly, because Task.done is observable, you need to invoke the member to get the value. Here's the modified code:
this.tasksFiltered = ko.computed(function() {
return ko.utils.arrayFilter(this.tasks(), function(Task) {
return Task.done() === true;
});
});
The second solution has a problem with the method ko.utils.stringStartsWith.
ko.utils.stringStartsWith missing in release file.
You can use this code:
var stringStartsWith = function (string, startsWith) {
string = string || "";
if (startsWith.length > string.length)
return false;
return string.substring(0, startsWith.length) === startsWith;
};
See Issue Link
It might help you
if (myList().length > 0) {
var text= this.filter().toLowerCase();
return ko.utils.arrayFilter(myList(), function (item) {
if (item.Label.toLowerCase().indexOf(text) > -1 || item.Desc.toLowerCase().indexOf(text) > -1) {
return true;
}
else {
return false;
}
});
}

knockout variable not defined - scoping issue?

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;
});

Categories

Resources