Calling a function within a Nested Object - javascript

I have an object, nested within a parent object. The inner object has 2 functions and one needs to call the other. I assumed I could call this.theFunction(), but that doesn't seem to be the case.
var views = {
settings: {
init: function() {
this.doSomething(withThing);
},
doSomething: function(with) {
// NEVER GETS CALLED
}
}
};
In this case, this seems to reference a DOMWindow rather than the views.settings object as I was expecting. What am I missing?
UPDATE
The views.settings.init() function is called as a callback. An external process calls template.init(view, views.settings.init);. The latter argument is a callback. Within template.init(), the callback is simply called as callback(). For clarity (hopefully), here's a snippet of how we get to views.settings.init:
template.init(view, views.settings.init);
var template: {
init: function() {
callback();
}
}
What would cause the context to get lost and what can I do to get it back so that this references the views.settings object?

Try doing this:
var views = {
settings: {
init: function() {
var withThing = 'withThing';
this.doSomething(withThing);
},
doSomething: function(withThing) {
// NEVER GETS CALLED
alert(withThing)
}
}
};
views.settings.init();
Here's a jsfiddle

Related

Jquery execute two functions on click

I am trying to execute functions on click, Below is click button on HTML,
Insights.init() will execute on page load will give me some data from server, now with click on button, i need to pass variable to month function to filter data, and with click i want to execute all functions inside Insights()
var Insights = function() {
var initCheckColor = function(vari) {
console.log(vari);
}
var testFunction = function(vari) {
console.log('test');
}
return {
init: function() {
initCheckColor();
testFunction();
}
};
}();
jQuery(document).ready(function() {
Insights.init();
});
function month(vari) {
console.log("hoo");
return {
init: function() {
initCheckColor(vari);
testFunction();
}
};
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Month
Now problem is, i can see "hoo" printed on console when i click on link, but i also want to print it with execution of initCheckColor(vari) function, means i want output two times, but i could not output it,
How can i get output two times?
Problem: Is with this code
function month(vari) {
console.log("hoo");
//this block of code
return {
init: function() {
initCheckColor(vari);
testFunction();
}
};
// upto here
}
When you call the month function you are returning a object with a property named init Note: you are just returning a object and not executing the functions within the property. Also other issue is this property is a function which executes two other function, But those functions are not available in the current scope. As they are equal to Private methods for the Insights object.
Solution: Would be to re initialize the object with data just like how you are doing on page load.
I have fixed your code and added comments in the code where the changes were made.
var Insights = function() {
var initCheckColor = function(vari) {
console.log(vari);
}
var testFunction = function(vari) {
console.log('test');
}
return {
init: function(vari) { // have a input parameter during initialization.
initCheckColor(vari);
testFunction();
}
};
}();
jQuery(document).ready(function() {
Insights.init('something'); // I pass in the string "something" now this will be printed by the initCheckColor function.
});
function month(vari) {
console.log("hoo");
Insights.init(vari); // initialize the Insights object by passing in some value.
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Month

How can I use defined key in another key/value pair

I have this javascript object:
return {
AccDocs: {
query: function() {
...
},
deleteAndQuery: function() {
...
AccDocs.query(); //Error: AccDocs is not defined
}
}
}
But, it returns an error that says AccDocs is not defined.
How can I achieve something like this?
Variables and properties on objects are different things. You cannot access the property of an object without specifying which object you mean.
You can probably access it using the this keyword:
this.query();
Keeping in mind that the value of this will vary depending on how the function is called (when a.b.c.d.AccDocs.deleteAndQuery() is called, this inside deleteAndQuery will be AccDocs as it is the first object to the left of the last ., but if you were to first copy query to another variable and then call query(), pass it to setTimeout, or if you were to use call or apply then the value of this would change).
For more robustness (but less flexibility, since being able to change the context can be useful) you can store your object in a variable which you can access by name.
var AccDocs = {
query: function() {
...
},
deleteAndQuery: function() {
...
AccDocs.query();
}
};
return { AccDocs: AccDocs };
By using the this keyword:
return {
AccDocs: {
query: function() {
...
},
deleteAndQuery: function() {
...
this.query(); //Here
}
}
}

JS Revealing Pattern event undefined issue

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

How to get the right class context from a method when it is invoked from a callback

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');.

Passing collection.fetch as a named function to collection.bind does not work

I have two Backbone collections. I want to bind to the reset event one one. When that event is fired, I want to call fetch on the second collection, like so:
App.collections.movies.bind("reset", App.collections.theaters.fetch);
The second fetch never fires though. However, if I pass an anonymous function that calls theaters.fetch, it works no problem:
App.collections.movies.bind("reset", function () { App.collections.theaters.fetch(); });
Any idea why this might be the case?
Heres my full code. I'm not showing any of the models or collections, because it's a lot of code, but let me know if you think that might be the source of the problem:
var App = {
init: function () {
App.collections.theaters = new App.Theaters();
App.collections.movies = new App.Movies();
App.events.bind();
App.events.fetch();
},
events: {
bind: function () {
App.collections.theaters.bind("reset", App.theaterManager.assign);
App.collections.movies.bind("reset", function () { App.collections.theaters.fetch(); });
},
fetch: function () {
App.collections.movies.fetch();
}
},
collections: {},
views: {},
theaterManager: {
// Provide each model that requires theaters with the right data
assign: function () {
// Get all theaters associated with each theater
App.theaterManager.addToCollection("theaters");
// Get all theaters associated with each movie
App.theaterManager.addToCollection("movies");
},
// Add theaters to a collection
addToCollection: function (collection) {
App.collections[collection].each(function (item) {
item.theaters = App.theaterManager.getTheaters(item.get(("theaters")));
});
},
// Returns a collection of Theaters models based on a list of ids
getTheaters: function () {
var args;
if (!arguments) {
return [];
}
if (_.isArray(arguments[0])) {
args = arguments[0];
} else {
args = Array.prototype.slice.call(arguments);
}
return new App.Theaters(_.map(args, function (id) {
return App.collections.theaters.get(id);
}));
}
}
};
$(function () {
App.init();
});
This all has to do with function context. It is a common confusion with the way functions are called in Javascript.
In your first way, you are handing a function to be called, but there is no context defined. This means that whoever calls it will become "this". It is likely that the equivalent will be of calling App.collections.movies.fetch() which is not what you want. At least, I am guessing that is what the context will be. It is difficult to know for sure... it might be jQuery, it might be Backbone.sync. The only way to tell is by putting a breakpoint in the Backbone.collections.fetch function and print out the this variable. Whatever the case, it won't be what you want it to be.
In the second case, you hand it a function again but internally, you specify the context in which the function is called. In this case, fetch gets called with App.collections.theaters as the context.
... was that clear?

Categories

Resources