I have an object which contains an array that I then pass to another function in order for that function to use. The only thing is, when I go to access these variables, console.log says they are undefined. It's strange as when I log the whole array it ways the values are there but when I go to access the array element specifically, it returns undefined.
Here is my code:
googleMapsFunctions.prototype.calculateDistances = function() {
var that = this;
console.log(that.latLngArray);
var closeClubs = [];
var sortable = [];
var resultsArray = [];
jQuery(this.clubs).each(function(key, club) {
var clubLatLng = new google.maps.LatLng(club.latitude, club.longitude);
var distanceFromLoc = clubLatLng.distanceFrom(that, "", "");
//alert(distanceFromLoc);
//that.clubs[key].distance = distanceFromLoc;
//closeClubs.push(club);
});
closeClubs.sort(function(a, b) {
return a.distance - b.distance;
});
}
googleMapsFunctions.prototype.setLatLng = function() {
var that = this;
this.geocoder.geocode({'address' : this.location}, function(results, status) {
if(status === "OK") {
that.latLngArray.push(parseFloat(results[0].geometry.location.lat()));
that.latLngArray.push(parseFloat(results[0].geometry.location.lng()));
}
});
}
//Client Code
var googleMapsClass = new googleMapsFunctions(JSONItems, searchTerm);
googleMapsClass.setLatLng();
googleMapsClass.calculateDistances();
I am using console.log to print out the array (that.latLngArray) which gives the following:
I then click on the aray brackets and it takes me to the following (which is the correct information).
I just can't seem to access these variables and it says that they are undefined.
Can anyone see what is happening here?
Thanks
Simplest thing to do would be to just move the distance calculation inside the callback:
googleMapsFunctions.prototype.setLatLng = function() {
var that = this;
this.geocoder.geocode({'address' : this.location}, function(results, status) {
if(status === "OK") {
that.latLngArray.push(parseFloat(results[0].geometry.location.lat()));
that.latLngArray.push(parseFloat(results[0].geometry.location.lng()));
// now it's safe to check the distances
that.calculateDistances();
}
});
}
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>
I've read several different callback function documentation but unfortunately couldn't be success to get related variable. What I'm missing on here?
checkNo: function (callback) {
var comboQuery = Ext.ComponentQuery.query('[name=foocombo]')[0].getSelectedRecord();
var chkno = comboQuery.get('mychkno'); //Success to get this integer value
callback(chkno); //Error raises here. Debugger says "callback is not a function"
//if (callback) callback(chkno); //I've tried to check callback but did not work as well.
},
setFooCombo: function () {
var me = this;
var fooStore = Ext.getStore('fooComboStore');
var chkno = ''; //Trying to pass an empty value. Not sure if correct approach
var checkno = me.checkNo(chkno); //Trying to get returned value from above function to be able using on url.
fooStore.getProxy().setUrl(MyApp.Url() + '/foo/list?=' + checkno); //I need pass value that return from callback to here
if (typeof checkno === MyApp.NUMBER) {
fooStore.load();
}
// I've tried another way to set new URL as below but did not work too.
// me.checkNo(function (checkno) {
//fooStore.getProxy().setUrl(MyApp.Url() + '/foo/list?=' + checkno);
// if (typeof checkno === MyApp.NUMBER) {
// fooStore.load();
// }
// });
},
UPDATE: After Rahul Khandelwal's answer re-factored the functions and of course now works.
checkNo: function () {
var comboQuery = Ext.ComponentQuery.query('[name=foocombo]')[0].getSelectedRecord();
var chkno = comboQuery.get('checkno');
return chkno;
},
setFooCombo: function () {
var me = this;
var fooStore = Ext.getStore('fooComboStore');
var checkno = me.checkNo();
fooStore.getProxy().setUrl(MyApp.Url() + '/foo/list?=' + checkno);
if (typeof checkno === MyApp.NUMBER) {
fooStore.load();
}
},
While calling the function you are not using callback functionality. Change it to the normal function definition like below.
CheckNo: function () {
var comboQuery = Ext.ComponentQuery.query('[name=foocombo]')[0].getSelectedRecord();
var chkno = comboQuery.get('mychkno'); //Success to get this integer value
return chkno; //Error raises here. Debugger says "callback is not a function"
//if (callback) callback(chkno); //I've tried to check callback but did not work as well.
}
To understand how the callback works, please use below link:
This StackOverflow question
me.checkNo(function(){ return checkNo;})
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!
I am relatively new to Javascript OOP and have been attempting to build a class to handle some functionality within my application.
The issue I am having is that after I have initialized my class, which sets some values in the constructor, when I call a function of that class which should update some variables within that instance and return them, these variables are coming through as undefined outside of the instance.
Here is my code:
//Google Maps Javascript class
var googleMapsFunctions = function(clubs, location) {
this.clubs = clubs;
this.location = location;
this.latLng = new Array();
this.geocoder = new google.maps.Geocoder();
this.closeClubs = [];
}
googleMapsFunctions.prototype.setLatLng = function() {
this.geocoder.geocode({'address' : this.location}, function(results, status) {
if(status === "OK") {
this.latLng.push(results[0].geometry.location.k);
this.latLng.push(results[0].geometry.location.B);
}
});
}
googleMapsFunctions.prototype.calculateDistances = function() {
var sortable = [];
var resultsArray = new Array();
try {
//alert(this.latLng);
} catch(error) {
alert(error);
}
}
//Client code
var JSONItems = <?php echo $this->JSONItems; ?>;
var searchTerm = "<?php echo $this->searchTerm; ?>";
var googleMapsClass = new googleMapsFunctions(JSONItems, searchTerm);
googleMapsClass.setLatLng();
googleMapsClass.calculateDistances();
When I try and access the 'this.latLng' variable from outside of the instance, it is saying that it is undefined. It is defined and outputting correctly when I log the data from within the 'setLatLng' function though which makes me think this is an encapsulation issue?
Can anybody give me some advice as to why this may be occuring?
Thanks
Keep the reference to the prototypes "this" by setting this to an variable and using it inside the Geocoders callback
var googleMapsFunctions = function(clubs, location) {
this.clubs = clubs;
this.location = location;
this.latLng = new Array();
this.geocoder = new google.maps.Geocoder();
this.closeClubs = [];
}
googleMapsFunctions.prototype.setLatLng = function() {
var that = this;
this.geocoder.geocode({'address' : this.location}, function(results, status) {
if(status === "OK") {
that.latLng.push(results[0].geometry.location.k);
that.latLng.push(results[0].geometry.location.B);
}
});
}
I want to go through a JSON, if a certain condition applies then push some extra elements in that index.
I have this JS code:
$scope.addRoleToUser = function() {
var userid = $scope.selectedUser;
var tmpdata = [];
var index = 0;
//alert(userid);
angular.forEach($scope.users, function(data) {
if (data.id == $scope.selectedUser) {
tmpdata.push(data,{"roles":[{"id":"00","name":"newrole"}]});
}
else {
tmpdata.push(data);
}
index++;
});
$scope.users = tmpdata;
};
This is my initial JSON element:
$scope.users = [
{"id":"0","name":"User1","roles":[{}]},
{"id":"1","name":"User2","roles":[{}]},
]
I'm trying to get it to look like this after the function runs:
$scope.users = [
{"id":"0","name":"User1","roles":[{"id":"00","name":"newrole"}]},
{"id":"1","name":"User2","roles":[{}]},
]
But instead I'm getting this:
[{"id":"0","name":"User1","roles":[{}]},{"roles":[{"id":"00","name":"newrole"}]},{"id":"1","name":"User2","roles":[{}]}]
Just replace this inside your function
if (data.id == $scope.selectedUser) {
data.roles = [{"id":"00","name":"newrole"}];
}
Or, if you know that roles is not empty, you can do:
if (data.id == $scope.selectedUser) {
data.roles.push({"id":"00","name":"newrole"});
}
And after this line you can add your data to tmpdata!
That snippet now will look like this:
if (data.id == $scope.selectedUser) {
data.roles = [{"id":"00","name":"newrole"}]}); //or the other one
}
tmpdata.push(data);
Inside the forEach() callback you're just working with objects and as such, you can modify them directly inside the callback:
angular.forEach($scope.users, function(data) {
if (data.id == $scope.selectedUser) {
data.roles = [{"id":"00","name":"newrole"}];
}
});
Similarly you could modify almost anything of each entry by manipulating the respective data object.
Example Fiddle
The Array.prototype.push method is variadic: (https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push).
When you call tmpdata.push(a,b,c), you are in essence appending the array [a,b,c] to tmpdata.
You can also decompose the problem with something like:
$scope.addRoleToUser = function() {
var thisUserid = $scope.selectedUser;
function addRolesFor(user) {
if (user.id === thisUserId){ user.roles = [{"id":"00","name":"newrole"}] };
return user;
}
retrun $scope.users.map(addRoles);
}
Please use the map function that is appropriate for your environment (like _.map), because the Array.prototype.map method is not supported by all browsers.