Typescript : Lost scope of 'this' - node.js

I'm trying to assign a value to myImage. When I look at the js target file myImage doesn't even exist. Obviously this throws an error. How do I preserve the scope of this within typescript classes?
What I'm trying to do here is load an image using the Jimp library. Then have a reference to that loaded image to perform operations on, for example resize is a method of a loaded image inside Jimp, so I'd like to be able to call i.myImage.resize(100,100).
"use strict";
var Promise = require('bluebird');
var Jimp = require("jimp/jimp");
class Image{
public myImage:any;
constructor(newImage:string){
Promise.all([new Jimp(newImage)]).then(function(img){
this.myImage = img;
}).catch(function(e){
console.log(e);
})
}
}
var i = new Image('./jd.jpg');
console.log(i.myImage)
The output:
undefined
[TypeError: Cannot set property 'myImage' of undefined]
With Callbacks :
var Jimp = require("jimp/jimp");
class Image {
public myImage: any;
constructor(newImage: string, typeOfImage: string) {
var self = this;
new Jimp(newImage, function(e,img) {
self.myImage = img;
});
}
}
var i = new Image('./jd.jpg');
console.log(i.myImage) // outputs undefined

The Jimp constructor does not return a promise so your use of Promise.all() here is likely not doing what you want it to do. As some people get confused by this, promises do not have any magic powers to somehow know when the Jimp object has finished loading it's image so if that's what you're trying to do here, it will not work that way. The Jimp constructor returns a Jimp object, not a promise.
I don't see any particular reason to not just use the Jimp callback that you pass into the constructor and make your code work like this:
var Jimp = require("jimp/jimp");
class Image {
private myImage: any;
constructor(newImage: string, typeOfImage: string) {
var self = this;
new Jimp(newImage, function(img) {
self.myImage = img;
// you can use the img here
});
// you cannot reliably use the img here
}
}
But, doing it this way looks like it still leaves all sorts of loose ends because the outside world has no way of knowing when the img has finished loading and you aren't saving the Jimp object reference itself so you can't use any of the Jimp methods on this image. As I said in my comments, if you share the bigger picture for what you're trying to do here, then we can more likely help you with a more complete solution to your problem.

Related

Cannot read properties of undefined using "This" in JavaScript class

I'm using Node JS and ExpressJS to write my web server. I use JavaScript OOP fromfew time. I get an error running this class:
class myClass {
constructor(path) {
this.path = path;
}
myFunction(){
var fileControllerInstance = new FileController(this.path);
fileControllerInstance.fileExist(function(fileExist) {
if(fileExist){
console.log("file exist");
this.printLine("test");
}
else
return false;
});
}
printSTR(str){
console.log(str);
}
}
new myClass("filePath").myFunction();
module.exports = myClass;
Running this class I get an error on printSTR function. Error is the follow:
file exist
TypeError: Cannot read properties of undefined (reading 'printSTR')
Without this I get ReferenceError: printSTR is not defined. To solve my problem I need to create another class instance and to use that to call the function. Something like this:
new myClass("filePath").printSTR("test") instead to ``` this.printLine("test"); ```
Why using this my code not working? Thanks
Inside the function(fileExist), this has a different value than outside. To inherit the value inside, you must bind the function:
fileControllerInstance.fileExist(function(fileExist) {
...
}.bind(this));
You're calling this inside a callback. You can find this post useful to solve your issue.
Also you try to call printLine("test") but your method is printSTR(str)

NodeJS requiring modules, module.exports.X vs module.exports={x}

I'm having trouble understanding the difference between exporting modules like:
module.exports.getUserIP = function getUserIP(req) {
var ip = req.headers['x-forwarded-for'];
return ip;
}
Or just declaring it:
function getUserIP(req) {
// retrieve user IP from req object
// Build this function to be more accurate/use more sources.
var ip = req.headers['x-forwarded-for'];
return ip;
}
and exporting at the bottom:
module.exports = { getUserIP }
or even:
module.exports = {getUserIP:getUserIP}
or
module.exports = {'getUserIP':getUserIP}
My problem is: when i call the function getUserIP from another file:
var mainbody = require('./app.js');//getUserIP is in here.
const gl = require('geoip-lite');
var ax = require('axios');
module.exports.getloc = function getloc(req, ip, property) {
//return location from IP.
if (req) {
var ipGuest = mainbody.getUserIP(req); //HERE
} else {
var ipGuest = ip;
}....
I get an error message:
Error Message
However, when I use the FIRST method to export the function:
module.exports.getUserIP = function getUserIP(req) {
var ip = req.headers['x-forwarded-for'];
return ip;
}
Then it works perfectly.
What's the difference?
Better way is to use
module.exports = { getUserIP: getUserIP }
This way you can just look at the export statement at the end of your file and know which functions are being exported from a particular file
The module.exports = {getUserIP}; is nothing but a shorthand of the above syntax(ES6 Magic). What it typically does is allows you to write this way { getUserIP } if the key name to be same as function/variable name like { getUserIP: getUserIP } where getUserIP can be a variable or a function or a ES6 class.
All the examples you show will work properly, but they do have some different affects.
By default module.exports is already initialized to an empty object. So, when you do something like this:
module.exports.getUserIP = function() {...}
You are assigning a new property to the existing object that module.exports already pointed to. One advantage of this scheme is that you can easily add more properties the same way.
module.exports.getUserRegion = function() {}
This will add one more property to that same object without disturbing the first one you already added.
On the other hand, all of these are identical:
module.exports = {getUserIP: getUserIP}
module.exports = {'getUserIP':getUserIP}
module.exports = { getUserIP } // ES6 shorthand for the previous syntax
and, they all end up with the same result as each other, but they all replace module.exports with a new object that has your one new property in it.
If you then tried to add another property:
module.exports = {getUserRegion};
That would again assign a whole new object to module.exports and you would have just wiped out the object that previously had getUserIP on it. When assigning a new object, you would typically assign an object that had all your properties on it:
module.exports = {getUserIP: function() {...}, getUserRegion: function() {...}};
Thus, not wiping out something you had already put there.
All of your schemes should work fine as long as you aren't overwriting module.exports with a new object and thus overwriting the object that already had some of your methods on it.
To understand this exporting modules concept, just think module.export is a simple object. you can bind anything to that object as do with normal javascript objects.
Finally when you require that module by require('path to js') you will get that exported object. If you export number of items in your module you can return them back by giving the names of the tag.

Passing a parameter trough require-module to ES6 class in Nodejs

I am learning to use ECMAScript6 -styled classes in NodeJS (7.7.3). I have used this kind of programming style:
//app.js
var forecastHandler = require('./forecastHandler.js');
//forecastHandler.js
class ForecastHandler {
constructor() {}
}
module.exports = new ForecastHandler()
It has worked well until now, because I have to pass parameters to module.
//app.js
var forecastHandler = require('./forecastHandler.js')(3600);
//forecastHandler.js
class ForecastHandler {
constructor(cacheUpdateDelay) {}
}
module.exports = new ForecastHandler(cacheUpdateDelay)
I got this error: ReferenceError: cacheUpdateDelay is not defined.
Can I pass the parameter to ForecastHandler-module using ES6 styled classes and creating an object at module.exports? If I only export the class and create the object in app.js, code works, but it's syntax is ugly.
//app.js
var forecastHandlerClass = require('./forecastHandler.js');
var forecastHandler = new forecastHandlerClass(3600);
//forecastHandler.js
module.exports = ForecastHandler
EDIT: better examples
module.exports = new ForecastHandler(cacheUpdateDelay)
The trouble with this code is that it initialises the object when the code is first run.
require('./forecastHandler.js') means "execute all the code in forecastHandler.js and give me the exports object. This means that the JS engine tries to run new ForecastHandler(cacheUpdateDelay) when there is no cacheUpdateDelay created.
The simple way to do this is the one you provide. Load the class, then try to make a new instance of it. If you really want to one-line it, you can do this in app.js:
var forecastHandler = new (require('./forecastHandler.js'))(3600);
There are various other ways you could do this. The simplest involve not exporting a class but a function.
For instance, you could do this in your module file:
module.exports = cacheUpdateDelay => new ForecastHandler(cacheUpdateDelay);
// OR
module.exports = function(cacheUpdateDelay) {
return new ForecastHandler(cacheUpdateDelay);
};

trying to stringify sails.config.global, circular structure error

I'm getting an error when trying to stringify a global variable in sails:
TypeError: converting circular structure to JSON.
I know what the error means, but the question is, what's happening that's causing there to be a circular reference. And, why does it happen to my custom variable?
Then the next question is: how can I stringify the object the way I created it in globals.js?
In config/globals.js:
module.exports.globals = {
mystuff: {
Url: "http://localhost:8080",
APIKey: "2bb67717b99a37e92e59003f93625c9b"
}
}
In a hook initialize:
module.exports = function (sails) {
return {
initialize: function(cb) {
var str = JSON.stringify(sails.config.globals.mystuff);
}
}
}
This helped identify circular culprits:
Detecting and fixing circular references in JavaScript
Another portion of the app was injecting objects into the globals.

Jasmine spies only detect calls on methods called on Node.js exports object

I'm trying to spy on dependencies in a Node.js module from within a Jasmine spec, and I'm encountering some weird behaviour.
Jasmine spies on a method that resides in an object exposed by the exports object in the module. When I call the spied-on method in the module using
exports.umap()
the spy detects it. But when I call it in the module using a variable name I assigned to the same method
var umap = exports.umap
umap()
the spy doesn't detect it.
This seems weird to me given that var umap refers to the same method as exports.umap. Anyone have any ideas why this is? Code below.
(The whole reason I'm doing this is that I want to stub my module's dependencies without the need to design the module as a class and use dependency injection.)
// mapper.js
exports.umap = require('underscore').map;
var umap = exports.umap;
function map () {
return exports.umap([1,2,3], function(el) {console.log(el);return el * 2});
// umap() goes unnoticed by the spy
// return umap([1,2,3], function(el) {console.log(el);return el * 2});
}
exports.map = map;
.
// mapper_spec.js
var mapper = require ('../../lib/mapper');
var map = mapper.map;
describe('mapper', function() {
describe('map', function() {
it('calls umap', function() {
var mapped;
spyOn(mapper, 'umap').and.callThrough();
mapped = mapper.map();
expect(mapper.umap).toHaveBeenCalled();
expect(mapped).toEqual([2,4,6]);
});
});
});
UPDATE
I'm still curious about this issue, however https://www.npmjs.org/package/proxyquire made my whole "spying-on-things-attached-to-export-object" strategy moot.

Resources