Javascript Scope and this.Variable - javascript

So I have some javascript with the following (pseudo) structure. How do I set the this.last_updated variable of the parent function from the showUpdates function, without specifically referencing the name assignment (my_main_function).
var my_main_function = new main()
function main() {
this.last_updated;
function showUpdates(data){
//set this.last_updated=
// do Stuff
}
this.updateMain(){
$.ajax({
url:"/my_url/"
type:"POST",
datatype:"json",
data: {'last_updated':this.last_updated },
success : function(data) { showUpdates(data)},
error : function(xhr,errmsg,err) {
alert(xhr.status + ": " + xhr.responseText); },
});
}
}

Updated the code base one the comments:
There are two way of creating objects.
If you need to create the object multiple time you will do it like this:
var YourDefintinon = function() {
};
YourDefintinon.prototype.foo = function() {
};
obj1 = new YourDefintinon();
obj2 = new YourDefintinon();
obj1.foo();
If you only need it once in your code you can just do it like that:
var obj = {
};
obj.foo = function() {
};
foo();
So your would need the main only once your code would look like this:
Using Function.prototype.bind (and its polyfill for older browsers) to bind the showUpdates to the obj.
var main = {
last_updated : null
};
function showUpdates(data){
this.last_updated = data.update_time;
}
main.updateMain = function () {
//<< bind showUpdates to `this` and save the bound function in the local variabel showUpdates
var showUpdates = showUpdates.bind(this);
$.ajax({
url:"/my_url/"
type:"POST",
datatype:"json",
data: {'last_updated':last_updated },
success : showUpdates, //<< uses the showUpdates variable not the function
error : function(xhr,errmsg,err) {
alert(xhr.status + ": " + xhr.responseText);
},
});
};
As you don't want to make showUpdates accessible to others you could wrap the whole block into a function that is immediatly called:
var main = (function() {
var main = {
last_updated : null
};
function showUpdates(data){
this.last_updated = data.update_time;
}
main.updateMain = function () {
var showUpdates = showUpdates.bind(this);
$.ajax({
url:"/my_url/"
type:"POST",
datatype:"json",
data: {'last_updated':last_updated },
success : showUpdates,
error : function(xhr,errmsg,err) {
alert(xhr.status + ": " + xhr.responseText);
},
});
};
return main;
}());

Related

Object prototype doesn't recognize function

I have 3 functions on an object prototype:
Job.prototype.postData = function(url) {
this.getFormData();
var formdata = new FormData();
formdata.append("position", this.position);
formdata.append("team", this.team);
formdata.append("details", this.details);
$.post(url, formdata, function(response) {
alert(response.message);
window.location.href = '/';
});
};
Job.prototype.createNew = function() {
var newUrl = API_URL + "/add_listing";
this.postData(newUrl)
};
Job.prototype.update = function() {
var updateUrl = API_URL + "/edit_listing" + "?id=" + this.id;
this.postData(updateUrl)
};
I'm attaching the last two as event handling callbacks:
$('#list-button').on('click',job.createNew);
$('#list-button').on('click',job.update);
Both of those give a TypeError:
oop.js:38 Uncaught TypeError: this.postData is not a function
The problem is simply that the this context gets lost the way you're binding the event handler.
Either explicitly bind the context, or call the function in a way that preserves the context:
$('#list-button').on('click', function () { job.createNew(); });
$('#list-button').on('click', job.createNew.bind(job));
See How does the "this" keyword work?.

How to keep object in scope?

I am implementing the following functionality. The majority of the page is created once in my entryPage.php file. The "Stage" table is created through an AJAX call to stageArea.php. My issue is that my data object is created in a $(document).ready(function() {...} which cannot be accessed by my other functions outside of the $(document).ready(function() {...}.
entryPage.php
...HTML...
<script>
$(document).ready(function() {
var dataObject = new DataEntryObj(); // create the data object
$.post("../stageArea.php", {array : dataObject.dataArray}, function(data){
$('#stageArea').html(data);
});
}
var DataEntryObj = function(){
this.dataArray = [[0,0,0,0,0,0,0,3,0]];
....
}
function updateData(value, row, index){
alert("update:" + row + " " + index + " " + value); //values come in OK
alert(dataObject.dataArray[row][index]); // but the data Object isn't here
dataObject.dataArray[row][index] = value; // so this cannot be assigned
$.post("../stageArea.php", {array : dataObject.dataArray}, function(data){
$('#stageArea').html(data);
});
}
</script>
What is a good solution to keep the object in the same scope as the other functions?
Just declare the var outside of $(document).ready and then assign it's value without using var
var dataObject ;
$(document).ready(function() {
dataObject = new DataEntryObj(); // create the data object
......
});
This will now be equivalent to window.dataObject
You can define a global variable, but you should make sure that updateData tries to access dataObject after DOM becomes accessible
var dataObject, DataEntryObj;
DataEntryObj = function(){
this.dataArray = [[0,0,0,0,0,0,0,3,0]];
}
$(document).ready(function() {
dataObject = new DataEntryObj();
$.post("../stageArea.php", {array : dataObject.dataArray}, function(data){
$('#stageArea').html(data);
});
}
function updateData(value, row, index) {
// will have ref to dataObject changed by $(document)
}
You can not access dataObject varailable. Because you setted in function. You should create a global object variable and you can access everywhere.
var dataObject = { data: null };
$(document).ready(function() {
dataObject.data = new DataEntryObj(); // create the data object
$.post("../stageArea.php", {array : dataObject.data.dataArray}, function(data){
$('#stageArea').html(data);
});
}
var DataEntryObj = function(){
this.dataArray = [[0,0,0,0,0,0,0,3,0]];
}
function updateData(value, row, index){
alert("update:" + row + " " + index + " " + value);
alert(dataObject.data.dataArray[row][index]);
dataObject.data.dataArray[row][index] = value;
$.post("../stageArea.php", {array : dataObject.data.dataArray}, function(data){
$('#stageArea').html(data);
});
}

backbone marionette pass variable to view method

I have simple situation and can't understand why variable that I pass to function always undefined.
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
this.renderProgress('4'); //<== pass here
});
},
renderProgress: function (why) {
alert(why); //<== undefined
...
},
...
});
I expect that it equals '4'. In next step I want to pass "data" but now I realize that I can't pass anything.
Since you're invoking renderProgress on the return of $.getJSON you can simply provide the function reference to the done()method of the returned jQuery Promise. Your code would look like this:
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
$.getJSON('service/api.php/projects/' + id + '/progress')
.done(this.renderProgress);
},
renderProgress: function (data) {
alert(data);
...
},
...
});
If you'll need the view context inside renderProgress (like, for example, to refer to a view property), then provide done() a version of renderProgress that's bound to the view context:
$.getJSON('service/api.php/projects/' + id + '/progress')
.done(_.bind(this.renderProgress, this));
where _.bind is an UnderscoreJS function. Read more about it here.
You loose the context in $.getJSON done callback. Try this:
var ProjectItemView = Backbone.Marionette.ItemView.extend({
template: "#ProjectItemTemplate",
initialize: function () {
var id = this.model.get('project_id');
var _this = this;
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
_this.renderProgress('4'); //<== pass here
});
},
renderProgress: function (why) {
alert(why); //<== undefined
...
},
...
});
You don't have access to this inside " $.getJSON( " assign this to any variable and then call "renderProgress" method.
var currentObj = this;
$.getJSON('service/api.php/projects/' + id + '/progress').done(function (data) {
currentObj .renderProgress('4'); //<== pass here
});
because in your case this points to current object of that function and not to view object.

Scoping of 'this' in TypeScript

I have a very simple class, but already run into pain with the definition of ‘this’ in Typescript:
Typescript
/// <reference path='jquery.d.ts' />
/// <reference path='bootstrap.d.ts' />
module Problem {
export class Index {
detailsUrl: string;
constructor() {
$('.problem-detail-button').click((e) => {
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: this.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
Javascript
var Problem;
(function (Problem) {
var Index = (function () {
function Index() {
var _this = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(_this).closest('tr');
var problemId = $row.data('problem-id');
$.ajax({
url: _this.detailsUrl,
data: {
id: problemId
},
type: 'POST',
success: function (result) {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
}
});
});
}
return Index;
})();
Problem.Index = Index;
})(Problem || (Problem = {}));
Now the problem is that the line
var $row = $(this).closest('tr'); //this must be that of the callback
and this line
this.detailsUrl, //this must be the instance of the class
conflict in the meaning of 'this'
How do you handle the mixture of the 'this'?
module Problem {
export class Index {
detailsUrl: string;
constructor() {
var that = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: that.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
Explicitly declare that = this so you have a reference for that.detailsUrl, then
don't use a fat arrow for the click handler, so you get the correct this scope for the callback.
You need to fallback to the standard way of javascript. i.e store the variable as :
var self = this;
Then you can use function instead of ()=> and use this to access variable in callback and self to access the instance of the class.
Here is the complete code sample:
module Problem {
export class Index {
detailsUrl: string;
constructor() {
var self = this;
$('.problem-detail-button').click(function(e){
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: self.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
// Creating
var foo:any = {};
foo.x = 3;
foo.y='123';
var jsonString = JSON.stringify(foo);
alert(jsonString);
// Reading
interface Bar{
x:number;
y?:string;
}
var baz:Bar = JSON.parse(jsonString);
alert(baz.y);
And your generated javascript:
var Problem;
(function (Problem) {
var Index = (function () {
function Index() {
var self = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(this).closest('tr');
var problemId = $row.data('problem-id');
$.ajax({
url: self.detailsUrl,
data: {
id: problemId
},
type: 'POST',
success: function (result) {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
}
});
});
}
return Index;
})();
Problem.Index = Index;
})(Problem || (Problem = {}));
var foo = {
};
foo.x = 3;
foo.y = '123';
var jsonString = JSON.stringify(foo);
alert(jsonString);
var baz = JSON.parse(jsonString);
alert(baz.y);
If you're only supporting browsers that have .addEventListener, I'd suggest using that to associate your data with your elements.
Instead of implementing your code, I'll just give a simple example.
function MyClass(el) {
this.el = el;
this.foo = "bar";
el.addEventListener("click", this, false);
}
MyClass.prototype.handleEvent = function(event) {
this[event.type] && this[event.type](event);
};
MyClass.prototype.click = function(event) {
// Here you have access to the data object
console.log(this.foo); // "bar"
// ...and therefore the element that you stored
console.log(this.el.nodeName); // "DIV"
// ...or you could use `event.currentElement` to get the bound element
};
So this technique gives you an organized coupling between elements and data.
Even if you need to support old IE, you can shim it using .attachEvent().
So then to use it, you just pass the element to the constructor when setting up the data.
new MyClass(document.body);
If all the logic is in your handler(s), you don't even need to keep a reference to the object you created, since the handlers automatically get it via this.
I normally bind this to a variable as soon as I have it in the scope I want.
However the this you are after could be found like this:
constructor() {
var class_this=this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var callback_this=e.target;
Late to the thread, but I have something different to suggestion.
Instead of:
var $row = $(this).closest('tr'); //this must be that of the callback
Consider using:
var $row = $(e.currentTarget).closest('tr');
As in this example, anywhere you might want to use this in a jQuery callback, you have access to a function parameter you can use instead. I would suggest that using these parameters instead of this is cleaner (where "cleaner" is defined as more expressive and less likely to be turned into a bug during future maintenance).
module Problem {
export class Index {
constructor() {
$('.classname').on('click',$.proxy(this.yourfunction,this));
}
private yourfunction(event){
console.log(this);//now this is not dom element but Index
}
}
}
check about jquery.proxy().
just remind you there is another way.

javascript classes scope and anonymous functions

I have a javascript class declared as shown below.. My problem is the aonymous function does not see the scope of the class. I can not reference the bb_obj from within the ajax call load callback..
Is there a way to do this?
Thanks in advance..
dojo.declare("sop.quote", null,
{
bb_obj : new Object,
stage1 : function()
{
dojo.xhrPost(
{
url : 'go/module_service/transport.php',
content : this.bb_obj,
handleAs : 'xml',
load : function(xml)
{
var status = xml.getElementsByTagName("STATUS")[0].childNodes[0].nodeValue;
var message = xml.getElementsByTagName("MESSAGE")[0].childNodes[0].nodeValue;
this.bb_obj.message = message;
},
error : function()
{
}
}
}
}
this inside a XHR callback function refers to the XHR object. The only way to refer to bb_obj is by directly referring to the created object, in the same scope as the function. Because objects are passed by reference, the code below works as intended.
Note to avoid confusion, I've declared the object using var bb_obj_obj={}. The bb_obj property refers to bb_obj_obj:
bb_obj_obj.message is changed
bb_obj points to bb_obj_obj, hence bb_obj.message refers to the same variable
Code:
var bb_obj_obj = {}; //new Object
dojo.declare("sop.quote", null,
{
bb_obj : bb_obj_obj,
stage1 : function()
{
dojo.xhrPost(
{
url : 'go/module_service/transport.php',
content : this.bb_obj,
handleAs : 'xml',
load : function(xml)
{
var status = xml.getElementsByTagName("STATUS")[0].childNodes[0].nodeValue;
var message = xml.getElementsByTagName("MESSAGE")[0].childNodes[0].nodeValue;
bb_obj_obj.message = message; //Without `this`
},
error : function()
{
}
}
}
}
An alternative method consists of saving this in a variable, eg. $this:
...
stage1 : function()
{
var $this = this;
dojo.xhrPost({
...
load : function(xml){
var status = xml.getElementsByTagName("STATUS")[0].childNodes[0].nodeValue;
var message = xml.getElementsByTagName("MESSAGE")[0].childNodes[0].nodeValue;
$this.bb_obj.message = message; //Using `$this` instead of `this`
},
...
The typical Javascript practice, in this case, is to wrap the whole thing in an an unnamed function. All local variables of this function will be accessible to every object/function declared therein. By immediately calling this unnamed function you make sure the code inside the function gets executed.
(function() {
var bb_obj = {}
dojo.declare("sop.quote", null, {
stage1 : function()
{
dojo.xhrPost(
{
url : 'go/module_service/transport.php',
content : this.bb_obj,
handleAs : 'xml',
load : function(xml)
{
var status = xml.getElementsByTagName("STATUS")[0].childNodes[0].nodeValue;
var message = xml.getElementsByTagName("MESSAGE")[0].childNodes[0].nodeValue;
bb_obj.message = message;
},
error : function()
{
}
}
}
})()

Categories