Javascript variable inheritance issue - javascript

I am having an issue with some code which I would expect to work. I have a variable defined outside a function and as such would expect that to be available to the function through inheritance. I console log the variable outside the function and get a value and console log inside the function and get undefined. I have used comments in the code to show these console logs. Any help here would be great. Please see code snippet below. Thanks Ant
for (var i = 0; i < parseResult.length; i++) {
var destination = parseResult[i].attributes.userInfo;
for (var i = 0; i < firebaseResult.length; i++) {
if (firebaseResult[i].$id == parseResult[i].attributes.facebookID) {
parseResult[i].attributes.convoID = firebaseResult[i].convoID;
console.log(firebaseResult[i].time); // this returns the timestamp value
parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
.then(function(lastMessage) {
console.log(firebaseResult[i].time); // this returns undefined
if (!(0 in lastMessage)) {
var returnValue = 'Matched on ' + firebaseResult[i].time;
} else if (0 in lastMessage) {
var returnValue = lastMessage[0].message;
}
return returnValue;
})
.catch(function(error) {
console.log("Error:", error);
})
}
}
}

It is often not reliable to use loop iterator to access things in async callback, because when the callback come back, i would have been increased to the value that let it exit the loop.
The fix is assigning it to a variable for anything you want to hold.
console.log(firebaseResult[i].time); // this returns the timestamp value
var time = firebaseResult[i].time;
parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
.then(function(lastMessage) {
console.log(firebaseResult[i].time); // this returns undefined
console.log(time);

It's because you're in the callback of your FirebaseAPI.getLastMessage() call. If you log this (your scope) in it, you'll get something like a FirebaseAPI object or something.
What you can do is the classic var self = this; trick to keep your context stored in a variable accessible through scopes.
This would look like:
for (var i = 0; i < parseResult.length; i++) {
var destination = parseResult[i].attributes.userInfo;
for (var i = 0; i < firebaseResult.length; i++) {
if (firebaseResult[i].$id == parseResult[i].attributes.facebookID) {
parseResult[i].attributes.convoID = firebaseResult[i].convoID;
console.log(firebaseResult[i].time); // this returns the timestamp value
// Store your context here
var self = this;
this.results = firebaseResult[i].time;
parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
.then(function(lastMessage) {
console.log(firebaseResult[i].time); // this returns undefined
// Get your values
console.log(this.results); // this returns your values.
if (!(0 in lastMessage)) {
var returnValue = 'Matched on ' + firebaseResult[i].time;
} else if (0 in lastMessage) {
var returnValue = lastMessage[0].message;
}
return returnValue;
})
.catch(function(error) {
console.log("Error:", error);
})
}
}
}
I see in your tags that you're using Angularjs, so you could use the $scope object to store your scope variable and easily deal with promises and such.
Hope this helps :)

Related

.map() unable to access Object's this.function

Thanks in advance for any responses:
I don't think this is a duplicate: I reviewed that article in the first comment, that is just a general breakdown of objects and using "this" within javascript.
My other this.function's perform just fine, so I at least have the basics of JS Obj's figured out.
This issue is related to using .map() with a this.function within a constructed object.
The following Google Appscript code uses .map() to update a string in a 2d array. [[string, int],[string, int]]
For some reason, when using .map() it is am unable to access the function "this.removeLeadingZero". If that same function is placed outside of the OBJ it can be called and everything works just fine. For some reason the system claims row[0] is an [object, Object] but when I typeof(row[0]) it returns "string" as it should.
Error: TypeError: Cannot find function removeLeadingZero in object [object Object]. (line 106, file "DEEP UPC MATCH")
Is there any issue using this.function's with .map() inside an object or am I using an incorrect syntax?
function test2DMapping(){
var tool = new WorkingMappingExample()
var boot = tool.arrayBuild();
Logger.log(boot)
}
function WorkingMappingExample(){
this.arr= [["01234", 100],["401234", 101],["012340", 13],["01234", 0422141],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3]];
//mapping appears faster that normal iterations
this.arrayBuild = function(){
var newArray1 =
this.arr.map( function( row ) {
**var mUPC = removeLeadingZero2(row[0])** //working
**var mUPC = this.removeLeadingZero(row[0])** // not working
var index = row[1]
Logger.log(mUPC + " " + index)
row = [mUPC, index]
return row
} )
return newArray1;
};
}; //end of OBJ
//THE NEXT 2 FUNCTIONS ARE WORKING OUTSIDE OF THE OBJECT
function removeLeadingZero2(upc){
try {
if (typeof(upc[0]) == "string"){
return upc.replace(/^0+/, '')
} else {
var stringer = upc.toString();
return stringer.replace(/^0+/, '')
}
} catch (err) {
Logger.log(err);
return upc;
}
}
function trimFirstTwoLastOne (upc) {
try {
return upc.substring(2, upc.length - 1); //takes off the first 2 #'s off and the last 1 #'s
} catch (err) {
Logger.log(err);
return upc;
}
}
Inside the function that you pass to map, this doesn't refer to what you think it does. The mapping function has its own this, which refers to window, normally:
var newArray1 = this.arr.map(function(row) {
// this === window
var mUPC = this.removeLeadingZero(row[0]);
var index = row[1];
Logger.log(mUPC + " " + index);
return [mUPC, index];
});
You have four options:
Array#map takes a thisArg which you can use to tell map what the this object in the function should be:
var newArray1 = this.arr.map(function(row) {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
}, this); // pass a thisArg
Manually bind the function:
var newArray1 = this.arr.map(function(row) {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
}.bind(this)); // bind the function to this
Store a reference to the outer this:
var self = this;
var newArray1 = this.arr.map(function(row) {
// self === (outer this)
var mUPC = self.removeLeadingZero(row[0]);
// ...
});
Use an arrow function:
var newArray1 = this.arr.map(row => {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
});
Additionally, you could stop using this and new.
I have solved this issue and below is the answer in case anyone else runs into this:
this needs to be placed into a variable:
var _this = this;
and then you can call it within the object:
var mUPC = _this.removeLeadingZero(row[0])
Javascript scope strikes again!

call user function in foreach loop

i have understand that i need to change the global scope of this, because in the loop this refers to the window object. But if i try to define a variable in my foreach loop via a function its not working and i dont know why although my functio returns the correct value :(
// simple class for xml import
function io() {
this.vertexes = [];
this.getVertexByID = function(id) {
this.vertexes.forEach(function(entry) {
if (id == entry.id) {
// correct element found, displayed and returned
console.log(entry);
return entry;
}
});
}
this.importXML = function(xmlString) {
cells = this.xmlToJson(xmlString);
var parent = graph.getDefaultParent();
var _this = this;
graph.getModel().beginUpdate();
try {
// addEdges
cells.XMLInstance.Edges.Relation.forEach(function(entry) {
// both will be empty but i dont understand why :(
fromVertex = _this.getVertexByID(entry.fromNode);
toVertex = _this.getVertexByID(entry.toNode);
var e1 = graph.insertEdge(parent, null, '', fromVertex, toVertex);
});
} finally {
graph.getModel().endUpdate();
}
}
Returning a value in a forEach callback has no effect. It certainly is not the return value of the function that the forEach is part of.
So change this:
this.vertexes.forEach(function (entry) {
if(id==entry.id){
//correct element found,displayed and returned
console.log(entry);
return entry;
}
});
to this:
return this.vertexes.find(function (entry) {
return id==entry.id;
});

Pull specific field from nth index of Knockout observableArray of objects

I have a page that displays a list of file templates built using the following method.
var loadCustomTemplate = function () {
loadBaseTemplate();
var res = 0;
for (i = 0; i < self.GetSeam().length; i++) {
var a = self.count() + 1;
self.count(a);
res = self.GetSeam()[i].FileFormat.split("_");
if (res.length == 4) {
var ap = res[3].split('.');
self.append(ap[0]);
} else {
self.append("");
}
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(self.append()),
SequenceNumber: ko.observable(a)
}
self.CustomTemplate.push(obj);
}
self.count(0);
};
The user is then allowed to edit the fields as needed. They can also add records or remove them as needed. The method to add a record is as follows.
self.addTemplate = function () {
var count = self.CustomTemplate().length + 1;
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(""),
SequenceNumber: ko.observable(count)
}
self.CustomTemplate.push(obj)
};
Once those updates are made they can save the updated CustomTemplate. This uses ajax that is not important to this question. The save method calls a validation method that is supposed to check to make sure there are no duplicate FileFormat fields in the object array. This is what I have, but it is failing.
var validateTemplates = function() {
for (i = 0; i < self.CustomTemplate().length; i++) {
var checkVal = self.CustomTemplate()[i].FileFormat;
var checkSeq = self.CustomTemplate()[i].SequenceNumber;
for (j = 0; j < self.CustomTemplate().length; j++) {
if (checkSeq !== self.CustomTemplate()[j].SequenceNumber ){
if (checkVal+"" === self.CustomTemplate()[j].FileFormat) {
if (checkSeq == self.CustomTemplate()[j].SequenceNumber ){
return false;
}
}
}
}
return true;
};
The problem is that when checking self.CustomTemplate()[i].FileFormat and self.CustomTemplate()[i].SequenceNumber it isn't reflecting the data displaying on the page or the data being sent to the controller (MVC 4). If I put either of those in an alert it is showing a function. How do I access the data in those specific fields for comparison?
Thanks in advance.
If I put either of those in an alert it is showing a function.
That's because you're doing this kind of thing:
var checkVal = self.CustomTemplate()[i].FileFormat;
FileFormat is the result of ko.observable(...), which returns a function, so checkVal in fact does contain a function.
The solution is for all those cases to do this:
var checkVal = self.CustomTemplate()[i].FileFormat(); // Parentheses at the end!
The parentheses execute the observable function, and if you do so without parameters you "get" the value of that observable. (If you would pass in a value, it would "set" the observable to that value.)

How can I call the second sequential promise design pattern method in a loop in angularjs? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
New to angularjs and trying out the promise pattern for the first time -
I have a service utility inside which I have this method -
this.getData= function(url){
var defer = $q.defer();
$http({method: 'GET', url: url}).
success(function(data, status){
defer.resolve(data);
})
.error(function(data, status) {
defer.reject(status);
});
return defer.promise;
};
Now inside my controller, I am calling a method called A()
var A = function () {
$scope.myobjectArray = [];
return utility.getData("some url").then(funciton(data)
{
for (i = 0; i < data.length; i++) {
$scope.myobjectArray.push(data[i].attribute1, new Array());
}
}
).
then(function () {
return getTheSecondAttributeArray();
}).catch(function (status) {
//display error
});
};
var getTheSecondAttributeArray = function () {
for (i = 0; i < $scope.myObjectArray.length; i++) {
var secondAttributeArray = [];
var currentType = $scope.myObjectArray[i];
utility.getData("some url").then(function (response) {
for (j = 0; j < response.length; j++) {
//some response manipulation
secondAttributeArray.push(response[j].text);
}
currentType.secondAttribute = secondAttributeArray;
}).catch(function () {//catch error, display message
})
}
}
However, it looks like that the last element of the $scope.myobjectArray (n-1th element) is only getting populated. Also, the secondAttributeArray that this last element contains is a concatenated array of all secondAttributes for all objects of the $scope.myobjectArray.
Cannot figure out what can I change here.
EDIT:
When I tried accessing $scope.myObjectArray[j] inside the 'then' function, it said $scope.myObjectArray[j] was undefined. --> And so I created a currentType variable and assigned $scope.myObjectArray[j] to it and that was easily accessible inside the 'then' function. Weird!
Also, I see that only the last object of the $scope.myObjectArray gets values and not the rest. The rest of the objects in the array are empty
Any help is appreciated.
var myObject = function(firstattribute, secondAttribute){
this.firstattribute = firstattribute;
this.secondAttribute = secondAttribute;
}
The explanation here by Beehive (Angularjs $q.all) is something that I am facing. I only get the last loop's data.
The issue is all in function closures. When the get data returns, your currentType is the last one because the for j loop ended already. So what you need to do is move the code starting from utility.getData to a separate method passing the parameters of the currentType and the seccondAttributeArray so the closure will contain them as parameters of the function and not change them as the for j loop progresses.
for (i = 0; i < $scope.myObjectArray.length; i++) {
var secondAttributeArray = [];
var currentType = $scope.myObjectArray[i];
fillSecondAttributeArray($scope, secondAttributeArray, currentType);
}

How to retrieve Firebase's child's value into global scope?

I have a Firebase database set up like this:
>>XXX
>>>>dislike: 0
>>>>like: 1
In my web application, I can retrieve their value into console by:
var fb = new Firebase('https://xxx.firebaseio.com');
fb.child('like').once('value',function(snapshot){
console.log(snapshot.val());
});
fb.child('dislike').once('value',function(snapshot){
console.log(snapshot.val());
});
Now if I want to retrieve these values into the global scope, it will return undefined when I do this:
var fb = new Firebase('https://xxx.firebaseio.com');
var like = fb.child('like').once('value',function(snapshot){
return snapshot.val();
});
var dislike = fb.child('dislike').once('value',function(snapshot){
return snapshot.val();
});
Of course I have a silly solution to this problem, by putting entire script inside these two scopes - but it would be a disasters if I have hundreds of scopes to work with, and if I like to dynamically turn them on and off. Here is my solution:
var likeRef = new Firebase('https://xxx.firebaseio.com/like');
var dislikeRef = new Firebase('https://xxx.firebaseio.com/dislike');
likeRef.once('value',function(likeObj){
dislikeRef.once('value',function(dislikeObj){
var like = likeObj.val();
var dislike = dislikeObj.val();
});
});
Here is another answer suggested by Frank van Puffelen from the source <Passing variable in parent scope to callback function>, and it didn't quite work because seem to only work for script that is adding a new object in an array. Here is my attempt:
var like = 0;
var dislike = 0;
var val = 0;
var fb = new Firebase('https://xxx.firebaseio.com/');
function fb_like() {
fb.child('like').on('value', read_val);
return val;
}
function fb_dislike() {
fb.child('dislike').on('value', read_val);
return val;
}
function read_val(snapshot) {
var val = snapshot.val();
}
fb_like();
fb_dislike();
console.log(like);
console.log(dislike);
As you might expected, the console logs 0 and 0, instead of the values in like and dislike in firabase xxx database.
In fact, I took a step further and use array instead of integer value, and it still won't work:
var like = [0];
var dislike = [0];
var val = [0];
var fb = new Firebase('https://xxx.firebaseio.com/');
function fb_like() {
fb.child('like').on('value', read_val);
console.log('fb_like: ' + val[0]);
return val;
}
function fb_dislike() {
fb.child('dislike').on('value', read_val);
console.log('fb_dislike: ' + val[0]);
return val;
}
function read_val(snapshot) {
val[0].value = snapshot.val();
}
fb_like();
fb_dislike();
console.log('Like: ' + like[0]);
console.log('Dislike: ' + dislike[0]);
The console will logs:
fb_like: 0
fb_dislike: 0
Like: 0
Dislike: 0
This means probably means only adding (pushing) new objects into an array will work on a global scope, changing the value of an object will only effect the local scope.
Then, I realized even adding (pushing) new objects into an array cannot effect the global scope. Here is my attempt:
var like = 0;
var likeObj = [];
var fb = new Firebase('https://xxx.firebaseio.com/');
function fb_like() {
fb.child('like').on('value', read_like);
console.log('fb_like: ' + likeObj[0]);
return likeObj;
}
function read_like(snapshot) {
likeObj.push(snapshot.val());
console.log('likeObj: ' + likeObj[0]);
}
fb_like();
like = likeObj[0];
console.log('Like: ' + like);
As a result, the console logs:
fb_like: undefined
Like: undefined
likeObj: 1
This probably means the read_like() isn't effecting scopes larger than itself, event with array.push command.

Categories