I have a javascript code
Html5Template_300x250 = function(config) {
this.config = config;
var self = this;
adkit.onReady(this.init());
};
Html5Template_300x250.prototype = {
// Function That Creates Element Var
d: function(id) {
return document.getElementById(id);
},
// Initialize DCO HTML5 template
init: function() {
adkit.onReady(this.handleSVData);
},
handleSVData: function() {
var myData = adkit.getSVData("varName");
alert(myData);
this.startAd();
},
startAd: function(data) {
alert("test2");
}
}
In the above code i have used an external javascript adkit.js and using that method in my code. The initial method is started as
adkit.onReady(this.init());
It is calling a init function and which is then calling other methods including handleSVData which is getting a value from the json file which is in the root folder as
var myData = adkit.getSVData("varName");
The part of the code is working fine but after that line i am calling another method
this.startAd();
But this method is not working and i am getting error
TypeError: this.startAd is not a function
I am not good in javascript and giving me headaches can someone explain me why it is so complicated and what i am doing wrong here ??
When handleSVData is called by adkit it is called in the scope that is not an instance of Html5Template_300x250 - that is why this does not have startAd method.
As for adkit.onReady(this.init()); line.
adkit.onReady expects a function as a parameter. It stores this function variable and calls when it is time for onReady event. This is set correctly by adkit.onReady(this.handleSVData); line. this.init(), however, is a call to init function and your line adkit.onReady(this.init()); passes to adkit.onReady whatever init returns. But it does not return anything - you are passing undefined as parameter.
init: function() {
var template = this;
adkit.onReady(function(){
template.handleSVData();
});
},
And change line
adkit.onReady(this.init());
to
this.init();
Related
I have a weird situation, I'm working with a 3rd party API and I can use JavaScript but all my code has to be a return function in order for it to work, here is how my app looks like, it gets plugged into their system:
app.js
(function() {
var myVar1 = null;
var globalFunction = function(){
alert('TEST');
}
return {
test: null,
requests: {
},
events: {
'app.activated': 'initApp'
},
insideFunction: function(item){
//some code
},
initApp:function(){
//some code
//I can set the gobal variables using varName = Value
//I can set the return variables using this.varName = Value
//I can call the return functions using this.insideFunction()
//the entire app is basically run from inside 'return'
}
};
}());
I can access the global vars / function from inside the return, but how can I do it vice-versa? I need to call insideFunction from the globalFunction.
Assign the object you're returning to a variable before returning it.
var api = {
test: null,
...
};
var globalFunction = function() {
api.insideFunction();
};
return api;
I am using the modular design pattern for JS and I keep running into issues when using arguments bound functions. I have a particular function that I would like to bind to different events to keep from having to write the function for each bound event. The only difference in the function, or the argument, is the table that will be updated. The problem is that when I build a function with the arguments I need and pass those arguments to bound events, I get an undefined error, in the console, on load. Keep in mind, I want to stick with this design pattern for the security it offers.
Here is my JS:
var Users = (function(){
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
$addRoleForm.submit(ajaxUpdate(event, $rolesTableBody));
function ajaxUpdate(event, tableName) {
event.preventDefault();
event.stopPropagation();
var url = this.action;
var data = $(this).serialize();
var $this = $(this);
$.ajax({
type: 'POST',
url: url,
dataType: 'json',
data: data,
success: function(data) {
if(data.st === 0){
$messageContainer.html('<p class="alert alert-danger">' + data.msg + '</p>');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
} else {
$messageContainer.html('<p class="alert alert-success">' + data.msg + '</p>');
tableName.fadeOut().html('').html(data.build).fadeIn();
$this.find('input').val('');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
}
},
error: function(xhr, status, error){
console.log(xhr.responseText);
}
});
}
})();
Here is the error I get in the console, on load:
Uncaught TypeError: Cannot read property 'preventDefault' of undefined
I have tried to bind the event like this: $addRoleForm.on('submit', ajaxUpdate(event, $rolesTableBody)); and receive the same results.
Any ideas how to fix this?
You're seeing that issue, because the way you have it written now, ajaxUpdateexecutes, returns undefined and THEN passes undefined to the event listener, so you're basically doing this: $addRoleForm.submit(undefined).
2 Choices here:
1) You can wrap it in an anonymous function:
$addRoleForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someValue);
});
$someOtherForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someOtherValue);
});
2) You can set the first argument in-advance using bind:
$addRoleForm.submit(ajaxUpdate.bind($addRoleForm, someValue));
$someOtherForm.submit(ajaxUpdate.bind($someOtherForm, someOtherValue));
Using this way, you're binding the value of this to be $addRoleForm, setting the first argument to always be someValue, so it's the same as:
ajaxUpdate(someValue, event) {
//value of "this" will be $addRoleForm;
}
To pass the event, and the custom argument, you should be using an anonymous function call
$addRoleForm.submit(function(event) {
ajaxUpdate(event, $rolesTableBody));
});
This is by far the easiest and most readable way to do this.
What you're doing right now equates to this
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
var resultFromCallingFunction = ajaxUpdate(event, $rolesTableBody); // undefined
$addRoleForm.submit(resultFromCallingFunction);
Where you're calling the ajaxUpdate function, as that's what the parentheses do, and pass the returned result back to the submit callback, which in your case is undefined, the default value a function returns when nothing else is specified.
You could reference the function, like this
$addRoleForm.submit(ajaxUpdate);
but then you can't pass the second argument
The question refers to the Revealing Module pattern. Benefit of using this design is readability. Going with the anon function may work, but defeats the overall purpose of the module pattern itself.
A good way to structure your module to help maintain your scope is to setup helper functions first, then call a return at the end.
Example use case with events:
var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init
}
}().runMe();
Helps to "modularize" your workflow. You are also writing your revealing pattern as an IIFE. This can cause debugging headaches in the future. Editing the IIFE to instead invoke via the return is easier to maintain and for other devs to work with and learn initially. Also, it allows you to extend outside of your IFFE into another Module, example:
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.runMe();
anotherFunc();
}
return {
addClothes: init
}
}().addClothes();
I hope this helps to give you a better understanding of how/when/why to use the JS revealing pattern. Quick note: You can make your modules into IIFE, that's not a problem. You just limit the context of the scope you can work with. Another way of doing things would be to wrap the var User and var Clothes into a main module, and then make that an IIFE. This helps in preventing polluting your global namespace.
Example with what I wrote above:
// MAIN APPLICATION
var GettinDressed = (function() {
// MODULE ONE
///////////////////////////
Var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init,
style: someFunc
}
}();
// MODULE TWO
//////////////////////////
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.style();
anotherFunc();
}
return {
dressUp: init
}
}();
// Define order of instantiation
User.runMe();
Clothes.dressUp();
}());
I have a simple JavaScript class like that :
function MySIOClass(io)
{
this.io = io
this.ns = this.io.of('/notif')
this.init()
}
MySIOClass.prototype.init = function (){
this.ns.on('connection', this.newClient)
}
MySIOClass.prototype.newClient = function (socket)
{
socket.on('msg', function (data){ this.handle_msg(data)})
}
MySIOClass.prototype.handle_msg = function (data)
{
// handle my message
}
I get stuck on the function newClient, each time a socket.io client send an event 'msg', the console triggers
TypeError: Object #<Socket> has no method 'handle_msg'
I tried to keep a reference of the operator this inside the function newClient like that :
MySIOClass.prototype.newClient = function (socket)
{
var c = this;
socket.on('msg', function (data){ c.handle_msg(data)})
}
But no luck too, i got the following error about namespace :
TypeError: Object #<Namespace> has no method 'handle_msg'
My class is exported via a module, everything works except when i try to add a listener with the on method of socket.io inside a class. I have correctly used the "new" operator when i instantiated my class.
Could you help me figure out what's happening ? i tried several things, but none of them worked.
Thanks for your time.
When you pass this.newClient to .on('connection', ...), you are passing just the function itself and losing the (this) context. You need to either create a wrapper function that can access the correct context to call newClient() properly or create a bound version of newClient.
First option:
MySIOClass.prototype.init = function (){
var self = this;
this.ns.on('connection', function(socket) {
self.newClient(socket);
});
}
Second option:
MySIOClass.prototype.init = function (){
this.ns.on('connection', this.newClient.bind(this));
}
Regarding the error inside newClient() itself, you are correct in that you have to use a "self"/"that" variable that you can use to access the correct context to call handle_msg().
I've been working on writing a custom jquery plugin for one of my web applications but I've been running into a strange error, I think it's due to my unfamiliarity with object-oriented programming.
The bug that I've been running into comes when I try to run the $(".list-group").updateList('template', 'some template') twice, the first time it works just fine, but the second time I run the same command, I get an object is not a function error. Here's the plugin code:
(function($){
defaultOptions = {
defaultId: 'selective_update_',
listSelector: 'li'
};
function UpdateList(item, options) {
this.options = $.extend(defaultOptions, options);
this.item = $(item);
this.init();
console.log(this.options);
}
UpdateList.prototype = {
init: function() {
console.log('initiation');
},
template: function(template) {
// this line is where the errors come
this.template = template;
},
update: function(newArray) {
//update code is here
// I can run this multiple times in a row without it breaking
}
}
// jQuery plugin interface
$.fn.updateList = function(opt) {
// slice arguments to leave only arguments after function name
var args = Array.prototype.slice.call(arguments, 1);
return this.each(function() {
var item = $(this), instance = item.data('UpdateList');
if(!instance) {
// create plugin instance and save it in data
item.data('UpdateList', new UpdateList(this, opt));
} else {
// if instance already created call method
if(typeof opt === 'string') {
instance[opt](args);
}
}
});
}
}(jQuery));
One thing I did notice when I went to access this.template - It was in an array so I had to call this.template[0] to get the string...I don't know why it's doing that, but I suspect it has to do with the error I'm getting. Maybe it can assign the string the first time, but not the next? Any help would be appreciated!
Thanks :)
this.template = template
Is in fact your problem, as you are overwriting the function that is set on the instance. You end up overwriting it to your args array as you pass that as your argument to the initial template function. It basically will do this:
this.template = ["some template"];
Thus the next time instance[opt](args) runs it will try to execute that array as if it were a function and hence get the not a function error.
JSFiddle
I'm using Class.js for creating classes.
I'm not getting the right context inside a method when invocked from a call back function
My code is
WordCloud = MyClass.extend({
init: function(data) {
var me = this;
(......).on("onComplete", this.draw);
},
show: function(word) {
alert(word)
},
draw : function(words){
console.debug(this); // prints element that triggred `onComplete` action
console.debug(words); // "Hi"
console.debug(me); // me is not defined
me.show(words) // Need to call this method
}
});
Problem is draw method is fired when an action is completed, but inside draw method this is not the actual class instance, but the element that triggred the callback action.
I can't pass exta arguments while calling this.draw as it is a call back function and onComplete has only one parameter.
How can I call the show method from draw?
If you do not have to support Internet Explorer 8 or lower, you can use bind():
init: function(data) {
var me = this;
(......).on("onComplete", this.draw.bind(this));
}
Otherwise, if you're already using jQuery, you can leverage $.proxy(), which works the same way:
init: function(data) {
var me = this;
(......).on("onComplete", $.proxy(this.draw, this));
}
I use a helper function for these cases.
function hitch(obj, func) {
return function() {
return obj[func].apply(obj, arguments || [])
};
}
To call it you would use hitch(this, 'draw'); instead of this.draw.
Or to make it even simpler you could add a simplified version to your base class
function hitch(func) {
var that = this;
return function() {
return that[func].apply(that, arguments || [])
};
}
And just call this.hitch('draw');.