I've struggled for a while seek for help. This is really strange. I want to access an object property, but this always throws an error:
TypeError : cannot read property template of undefined
But my application works properly. Just there is notification output if can't access template of undefined
//this is my object variabel
var login = {};
login.data = {
checkInput : formValidation,
userSchema : User,
template : 'pages/users/login',
}
// so I add new method which I call in different files
login.header = async(req, res, next) => {
/// in this section function I want to read property of template but it always return undefined
try {
// I have some code with read databases here
//some data i want to render
var data = {};
res.render(this.data.template,data);
// I've been also trying another way.
var template = login.data.template !== undefined ? 'page/users/login' : login.data.template;
res.render(login.data.template, data);
// both of above always return output, but can't read template of undefined
} catch(e) {
throw new Error(e);
}
}
It's because you're using an arrow function which loses the binding to login (the this you're accessing is an attempt to access the login object). Use a regular ES5 function.
login.header = async function(req, res, next) {...};
As found in the docs:
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
i've been trying to access with property with no array function
var login = {}
login.data = {
template : 'admin/pages/'
body : {}
};
login.getViews = function(req, res, next) {
// it'l throw an error
res.render(this.data.template, this.data.body);
// than i try with another way, it works
res.render(login.data.template, login.data.body);
}
so why i could't access with property this even i use no rows function??
Related
In my project I want to overwrite the res.render method to always add an option, no matter what other options there are. When I try to do it I get the error mentioned above. Here's my current middleware code:
app.use((_, res, next) => {
const oldRender = res.render;
res.render = (view, options, callback) => {
if (typeof options === "function") {
callback = options;
options = {};
}
options.stops = stops?.data || {};
oldRender.call(this, view, options, callback);
};
next();
});
It looks to me like the point of this code is to always pass a particular value to your templates without requiring every caller of res.render() to add that to the data they pass to the template.
The general-purpose way to solve this type of problem is to put a property in res.locals. The template system looks there in addition to the data passed to res.render() and this is the easy way to pass data to all your templates:
app.use((_, res, next) => {
res.locals.stops = stops?.data || {};
next();
});
If there's some reason that won't work for you, then the apparent problem to the solution you're trying is that you're using arrow function so the value of this you're using in oldRender.call(this, view, options, callback); is the lexical value of this, not res which is what you need it to be. You can change this:
oldRender.call(this, view, options, callback);
to this:
return oldRender.call(res, view, options, callback);
You can directly pass res instead of trying to use this and you should also return the return value (which will be res) to duplicate the original behavior.
Doc link on res.locals.
A few other answers about res.locals:
Difference between assigning to res and res.locals in node.js (Express)
Is this bad practice for res.locals? (Node.js, express)
I've tried to search over what seems to be the entire internet, but I'm still vexed by a problem with a JS class I'm writing for a Micro Service (still in learning a bit).
So, I try to call a class method on an instantiated object, and according to my knowledge and my (faulty I presume) unit tests it should work.
Well, I'll start with the error I receive:
GET /api/users 500 2.863 ms - 2649
TypeError: Cannot read property 'repository' of undefined
at list (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\controllers\user-controller.js:20:9)
at Layer.handle [as handle_request] (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\node_modules\express\lib\router\route.js:137:13)
(And a lot more).
The code calling code:
user-controller.js
'use strict';
var utils = require('./utils');
class UserController {
constructor(repository) {
this.repository = repository || {};
}
/**
*
* Lists all users.
*
* #param {object} req
* #param {object} res
*/
list(req, res) {
this.repository.list(function (err, users) {
if (err) return res.status(500).json(utils.createError(500));
if (Object.keys(users).length !== 0) {
res.json(users);
} else {
res.status(404).json(utils.createNotFound('user', true));
}
});
}
// more code
}
module.exports = UserController
Caller of controller
user-api.js
'use strict';
var express = require('express');
var UserController = require('../controllers/user-controller');
var router = express.Router();
module.exports = function (options) {
var userController = new UserController(options.repository);
router.get('/users', userController.list);
// Mode code
return router;
};
I really jave no idea on why this undefined in UserController.
Any help would be greatly appriciated.
When you do this:
router.get('/users', userController.list);
what gets passed to your router is just a reference to the .list method. The userController instance gets lost. This is not unique to routers - this is a generic property of how things are passed in Javascript. To understand further, what you are essentially doing is this:
let list = userController.list;
// at this point the list variable has no connection at all to userController
router.get('/users', list);
And, in Javascript's strict mode, when you call a regular function without any object reference such as calling list() above, then this will be undefined inside the function. That is what is happening in your example. To fix it, you need to make sure that your method is called with a proper object reference as in userController.list(...) so that the interpreter sets the this value appropriately.
There are multiple ways to solve this problem:
Make your own function wrapper
router.get('/users', function(req, res) {
userController.list(req, res);
});
This works in any version of Javascript.
Using .bind() to make a wrapper for you that calls it with the right object
router.get('/users', userController.list.bind(userController));
This works in ES5+ or with a .bind() polyfill.
Use an ES6 arrow function shortcut
router.get('/users', (...args) => userController.list(...args));
This works in ES6+
Personally, I prefer the .bind() implementation because I think it's just simpler and more declarative/clearer than any of the others and the ES6 "shortcut" isn't really shorter.
router.get() won't call your class the way you think it will. You are giving it a reference to a function which it will call in the context of the router.get meaning, it won't be in the context of your userController.
You can fix this by doing:
router.get('/users', function(){userController.list(...arguments)});
In other words, don't have express use userController's reference to list, have express use a closure that will have userController call list with the given arguments.
I'm trying to synchronize the data attributes of a javascript object to Firebase. However, when I attach methods to my object Firebase throws an error:
Error: Firebase.set failed: First argument contains a function in property
I'm looking for a nice clean way to sync my objects without having to strip out all the methods, or create a data attribute within the object.
My Javascript constructor
var MyClass = function() {
this.foo = "bar"; // an example attribute. No problem for Firebase
}
// add a method. Firebase will throw an error because of this method.
MyClass.prototype.myMethod = function () {};
Create a new object
var myInstance = new MyClass();
Firebase throws an error
firebase.database().ref().set(myInstance);
Any ideas for a clean way to make something like this work?
This should do the trick:
var json = JSON.parse(JSON.serialize(myInstance));
firebase.database().ref().set(json);
But in general, I'd recommend keeping your code separate from your data. Objects from your Firebase Database should not contain functions.
In the routes folder of a Node.Js app I have a file entries.js which has the following function:
exports.form = function(req, res){
res.render('post', { title: 'Post' });
};
Is it actually possible to launch something like this from another exports function in the same file, such as:
exports.something = function(req, res){
this.form(req.res);
};
Where this.form refers to exports.form function in the same file.
Thank you!
The value of this by default will point to the object whose member the function is. In this case, both functions are members of the same object, thus using this.form should work as expected.
The exceptions to this rule are when the function in question is used together with bind, call, or apply.
The following TypeError cropped up in some old code.
TypeError: Object #<Object> has no method 'findOne'
The Model that was affected recently had two new static methods defined and those methods referenced external models. After backing out the new static methods, I was able to determine the root cause to be the require statements of the external models. The pattern looks like the following:
var UserModel = require('./user');
var GroupSchema = new Schema({
name: String,
users: [{ type : Schema.ObjectId, ref: 'UserModel'}],
});
GroupSchema.statics.findSomeUsers = function(group, callback) {
this.find({name : session_user._id}, function(err, groups) {
UserModel.find({_id : {$in : group.users}}, function(err,patients) {
// do magic
});
});
};
module.exports = mongoose.model('GroupModel', GroupSchema);
There is a code fragment in the application that calls GroupModel.findOne({name:'gogo'}) that leads to the TypeError. when I remove the require statement for the UserModel in the GroupSchema, app code works again.
Why does Javascript start to think findOne() is an instance method with the addition of the require statement?
It's known node.js issue. It means that you have looping require somewhere in your code and node.js forbids it.
The right way to do it is by using mongoose.model method. So, instead of UserModel variable you shall use mongoose.model('UserModel'). So, when findSomeUsers will be called mondoose will fetch UserModel and invoke its find method.
GroupSchema.statics.findSomeUsers = function(group, callback) {
this.find({name : session_user._id}, function(err, groups) {
mongoose.model('UserModel').find({_id : {$in : group.users}}, function(err,patients) {
// do magic
});
});
};