I have an object with some added methods to its prototype:
var MyClass = function() {
this.name = '';
this.innerData = [];
this._callbacks = {};
};
MyClass.prototype.add = function(data) {
this.innerData.push(data);
};
MyClass.prototype.remove= function(data) {
//This part is working
for (var i=0; i < this.innerData.length; i++) {
if (this.innerData[i].desc === desc) {
this.innerData.splice(i, 1);
}
}
};
MyClass.prototype.on = function(evname, callback) {
if (!this._callbacks[evname]) {
this._callbacks[evname] = $.Callbacks();
}
this._callbacks[evname].add(callback);
};
What I want to do, is a way for triggering ANY methods loaded in the _callbacks object, without the need to do an ugly if (methodIsBound(method)) { .... within all methods of my class.
E.g.:
var c = MyClass();
c.on('add', function() { console.log("Adding data"); });
c.add(someData);
c.add(someData2);
So far I tried doing this adding a check in any method, asking if that method is registered in the callbacks, but that is cumbersome, as for any new method I'd need to add the if (which does not belong to that method functionality), and if any other developer wants to add functionality, they will have to know that additional code.
I checked custom events both in pure JS and jQuery, but I definitely not understanding how to do it.
Does anyone have an idea?
Something in the like (TESTED ON FIREFOX)?
The idea is to take any method in MyClass.prototype and wrap it inside a function that calls a callback and then the original method. So every time you call the method, the saved callback is called, is that what you need?
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8" />
<title>Untitled</title>
<script>
var MyClass = function() {
this.name = '';
this.innerData = [];
};
MyClass.prototype.add = function(data) {
this.innerData.push(data);
};
MyClass.prototype.remove= function(data) {
//This part is working
for (var i=0; i < this.innerData.length; i++) {
if (this.innerData[i].desc === desc) {
this.innerData.splice(i, 1);
}
}
};
MyClass.prototype.on= function(fname,callback) {
var originalFunction=MyClass.prototype[fname];
var givenCallback=callback;
MyClass.prototype[fname]=function() {
givenCallback.call();
var args = Array.prototype.slice.call(arguments);
originalFunction.apply(this,args);
}
};
var c = new MyClass();
c.on('add', function() { console.log("Adding data"); });
c.add("aaa");
c.add("bbb");
</script>
</head>
<body>
</body>
</html>
UPDATE
Whith "fixed" parameters and "this" passed to the callback:
MyClass.prototype.on= function() {
var onArgs = Array.prototype.slice.call(arguments);
var originalFunction=MyClass.prototype[onArgs[0]];
var givenCallback=onArgs[1];
var callbackArgs=onArgs.slice(2);
MyClass.prototype[onArgs[0]]=function() {
var args = Array.prototype.slice.call(arguments);
givenCallback.apply(this,callbackArgs);
originalFunction.apply(this,args);
}
};
var c = new MyClass();
c.on('add', function(a,b) { console.log("Adding data '"+a+"',"+b+" obj:"+this); },"called add",1);
c.add("aaa");
c.add("bbb");
FIDDLE: http://jsfiddle.net/mgcr0jxj/2/
Related
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>
var Lines = function(startXCon, endXCon,startYCon, endYCon)
{
this.drawCurve = function()
{
}
this.changeCurve = function(e)
{
//how can I call drawCurve from this method
}
}
The comment in my code explains the problem. Is this possible or are all methods private?
Like this:
var Lines = function(startXCon, endXCon,startYCon, endYCon){
var self = this; // store this as a variable to use in nested function
this.drawCurve = function(){}
this.changeCurve = function(e){
self.drawCurve(); //now call this.drawCurve()
}
}
<html>
<body>
var el = document.getElementById("tab");
var tab = Table(el, data);
tab.showData();
tab.takeData();
var PieChart=drawPieChart(canvas);
</body>
</html>
<script>
function Table(el, data) {
...
...
return{
showData: function(){
...
...
}
takeData: function(){
var myData=new Array();
for (var i = 0; i < val2; i++) {
myData[i] = document.getElementById('polja' + i).value;
}
}
...
...
};
}
function drawPieChart(canvas){
...
...
return{
getmyData(
);
...
...
};
}
</script>
how can i get myData in function "drawPieChart" except making myData global variable? thx
i was thinking in html make somthing like this PieChart.getMyData(Table.takeData); or something like that
Based on your edit, you can do this:
function Table(el, data) {
var myData = new Array(); // Not global but accessible to every function that gets returned here
return{
// removed unnecessary code
takeData: function(){
for (var i = 0; i < val2; i++) {
myData[i] = document.getElementById('polja' + i).value;
}
},
function drawPieChart(canvas){
// Can access myData here
}
}
}
First, the problems with your code:
You have a block of JavaScript sitting inside <body>. This is not standard, not allowed, and will fail. Specifically, it will be displayed as text on the page, rather than being executed as JavaScript. All JavaScript must be located in one of the following places: an inline <script> tag, an external file that you load with a <script> tag, an event attribute such as onload="...", or an attribute that can run JavaScript, such as href="javascript:..." on <a> elements.
You have a syntax error in the object you're returning from Table(); there is no comma separator between the hash key/value pairs (showData() and takeData()). The comma is required.
You have some uninitialized variables, including val2, data, and canvas. You probably just excerpted your code to omit the relevant initialization, but you should try to present complete self-contained code samples when asking questions on Stack Overflow.
With regard to your question, the object tab you are returning from Table() looks an awful lot like an instance object of a class in any OO language. JavaScript supports the OO paradigm via the prototype pattern, so a sensible approach would be to make Table a full class by defining its prototype. If you do this, you can make tab a full instance of Table by creating it with the new operator, and then you can store the myData array as an attribute on tab. Here's a random demonstration based on your sample code:
http://jsfiddle.net/awytnngu/
HTML:
<div id="tab">
<input id="polja0" value="def1"/>
<input id="polja1" value="def2"/>
<input id="polja2" value="def3"/>
</div>
JS:
function Table(el,data) {
this.el = el;
return this;
}
Table.prototype.showData = function() {
// ...
};
Table.prototype.takeData = function() {
this.myData = new Array();
var val2 = this.el.children.length;
for (var i = 0; i < val2; ++i)
this.myData[i] = document.getElementById('polja'+i).value;
};
Table.prototype.drawPieChart = function(canvas) {
alert(this.myData);
};
var el = document.getElementById('tab');
data = 'whatever';
var tab = new Table(el,data);
tab.showData();
tab.takeData();
canvas = 'whatever';
var PieChart = tab.drawPieChart(canvas);
Just to throw in another possible approach, much simpler than the prototype solution, you can take an OUT parameter on takeData() and then pass it as an argument to drawPieChart():
http://jsfiddle.net/uv8bh6nj/
HTML:
<div id="tab">
<input id="polja0" value="def1"/>
<input id="polja1" value="def2"/>
<input id="polja2" value="def3"/>
</div>
JS:
function Table(el,data) {
return {
showData:function() {
// ...
},
takeData:function(OUT) {
OUT.myData = new Array();
var val2 = el.children.length; // closure
for (var i = 0; i < val2; ++i)
OUT.myData[i] = document.getElementById('polja'+i).value;
}
};
}
function drawPieChart(canvas,myData) {
alert(myData);
}
var el = document.getElementById('tab');
data = 'whatever';
var tab = new Table(el,data);
tab.showData();
var takeDataOUT = {};
tab.takeData(takeDataOUT);
canvas = 'whatever';
var PieChart = drawPieChart(canvas,takeDataOUT.myData);
Can you please help answering this. Please not the contraints.
var myLib = {
var callback_one = function (result_from_web_service) {
console.log('callback_one');
};
var callback_one = function (result_from_web_service) {
console.log('callback_two');
};
var init = function () {
console.log('initializing...');
async_call_one(callback_one);
async_call_two(callback_two);
};
var doStuff = function () {
console.log('doStuff is called');
};
};
// User of my library
myLib.init();
myLib.doStuff();
// output
initializing...
doStuff is called
callback_one
callback_two
// What i need:
initializing...
callback_one
callback_two
doStuff is called
Constraint:
calling myLib.init shall not end up calling myLib.doStuff. i.e. myLib.init should be independent of myLib.doStuff
myLib.doStuff() should be called after myLib.init() and its callbacks are returned.
Thanks,
//You must change your API so init is async
//There is no way to have it wait until all initialization is done before it retuns
var init = function (initDone) {
console.log('initializing...');
var n = 0;
function serviceDone(){
n++;
if(n >= 2){ initDone() }
}
async_call_one(function(x){ callback_one(x); serviceDone() });
async_call_two(function(x){ callback_two(x); serviceDone() });
};
// User of my library
myLib.init(function(){
myLib.doStuff();
})
The way I parallelized those calls is very ad-hoc s not the most maintainable (there I need to keep the calls to serviceDone and the value of N in sync).. In the long run I would recommend using one of the many JS async programming libs out there.
hugomg has a good answer.
Yet I think it is really specific and could benefit a sort of workflow implementation, like this (approximately...):
function void() {}
var myLib = {
var g_flow = [];
g_flow[this.init] = [];
g_flow[this.init]["whendone"] = this.callback_one;
g_flow[this.init]["done"] = false;
g_flow[this.callback_one] = [];
g_flow[this.callback_one]["whendone"] = this.callback_two;
g_flow[this.callback_one]["done"] = false;
g_flow[this.callback_two] = [];
g_flow[this.callback_two]["whendone"] = this.doStuff;
g_flow[this.callback_two]["done"] = false;
g_flow[this.doStuff] = [];
g_flow[this.doStuff]["whendone"] = void;
g_flow[this.doStuff]["done"] = false;
var callback_one = function (result_from_web_service) {
console.log('callback_one');
};
var callback_one = function (result_from_web_service) {
console.log('callback_two');
};
var init = function () {
console.log('initializing...');
};
var doStuff = function () {
console.log('doStuff is called');
};
var flow_onward(hwnd) {
async_call(function(){ hwnd(); myLib.flow_onward(g_flow[hwnd]["whendone"]); });
}
flow_onward(this.init);
};
// User of my library
myLib.init();
myLib.doStuff();
Doing this way you can ensure the sequentiality and expand the numbers of callback as much as you want.
ps: this code has not been tested
I have written some javascript that I would to encapsulate in a closure so I can use it elsewhere. I would like do do this similar to the way jQuery has done it. I would like to be able to pass in an id to my closure and invoke some functions on it, while setting some options. Similar to this:
<script type="text/javascript">
_snr("#canvas").draw({
imageSrc : someImage.png
});
</script>
I have read a lot of different posts on how to use a closure to do this but am still struggling with the concept. Here is where I left off:
_snr = {};
(function (_snr) {
function merge(root){
for ( var i = 1; i < arguments.length; i++ )
for ( var key in arguments[i] )
root[key] = arguments[i][key];
return root;
}
_snr.draw = function (options) {
var defaults = {
canvasId : 'canvas',
imageSrc : 'images/someimage.png'
}
var options = merge(defaults, options)
return this.each(function() {
//More functions here
});
};
_snr.erase = function () {};
})(_snr);
When ever I try to call the draw function like the first code section above, I get the following error, '_snr is not a function'. Where am I going wrong here?
EDIT
Here is what I ended up doing:
function _snr(id) {
// About object is returned if there is no 'id' parameter
var about = {
Version: 0.2,
Author: "ferics2",
Created: "Summer 2011",
Updated: "3 September 2012"
};
if (id) {
if (window === this) {
return new _snr(id);
}
this.e = document.getElementById(id);
return this;
} else {
// No 'id' parameter was given, return the 'about' object
return about;
}
};
_snr.prototype = (function(){
var merge = function(root) {
for ( var i = 1; i < arguments.length; i++) {
for ( var key in arguments[i] ) {
root[key] = arguments[i][key];
}
}
return root;
};
return {
draw: function(options) {
var defaults = {
canvasId : 'canvas',
imageSrc : 'images/someimage.png'
};
options = merge(defaults, options);
return this;
},
erase: function() {
return this;
}
};
})();
I can now call:
<script type="text/javascript">
_snr("#canvas").draw({
imageSrc : someImage.png
});
</script>
Because you declared _snr as an object and not a function. Functions can have properties and methods, so there's various ways to achieve what you want, for example one of them would be say...
_snr = function(tag) {
this.tag = tag;
}
_snr.foo = function() {
//Code goes here
}
You can also pass the outer context into a closure to hide your variables from accidentally polluting the global namespace, so like...
(function(global) {
var _snr = function(tag) {
this.tag = tag;
}
_snr.foo = function() {
//Code goes here
}
//export the function to the window context:
global._snr = _snr;
})(window);
window._snr('#tag').foo('wat');
Happy coding.
Because your _snr is an object, not a function. You have to call it like this:
_snr.draw({
canvasId: '#canvas',
imageSrc: 'someImage.png'
});
When you do _snr('#canvas') that is a function call which is why you're getting that error. _snr is an object with some methods attached to it such as draw() and erase(). The reason jQuery is able to pass arguments into the $ is because they return the $ as a function object which is why we're able to pass it various selectors as arguments.
You are going wrong at the first line _snr = {}
It needs to be
_snr = function(){
selector = arguments[0]||false;
//snr init on dom object code
return _snrChild;
}
Im on a mobile phone but when im on a pc I will maybe fix the whole code c:
Here you have a snr object and that has erase and draw methods. What you intend to do is to write a _snr function which will get an id and return a wrapper object. That returned object should have erase and draw methods. so you can do
var returnedObject = _snr("my_id");
returnedObject.draw("image.png");