I'm simply trying to display a value in an input field with Jade (0.20.3) and Express (2.5.8):
input(name='username', type='text', id="username", value=username)
It is quite simple, but this throws an error when the value is undefined:
username is not defined
However, the documentation indicates:
When a value is undefined or null the attribute is not added, so this is fine, it will not compile 'something="null"'.
Is there something that I would have done wrong?
Short answer: use locals.someVar if you're not sure that someVar exists.
Longer Answer:
I think that Brandon's initial answer and last comment are correct (though the #{...} syntax isn't needed), but to elaborate a bit: There's a difference between passing in an a variable (technically, an object property) with a value of undefined, and not passing that variable at all.
Because Jade transforms your template into JS source and evals it (in the context of a with block), you have to make sure that you're not referring to any variables that haven't been passed in, or they'll be . This blog post has some background on undefined vs undeclared variables and ReferenceError.
Your Jade template should work correctly if you do one of these things:
// ok, even if req.session.username is not defined
res.render('index', { username: req.session.username })
// ditto
res.local('username', req.session.username);
res.render('index')
But these won't work:
res.locals(req.session) //if no username property exists
res.render('index', { /* no username */ } )
If it's not practical to manually pass in each parameter you might want to refer to, you can refer to the variable in your template as properties on the locals object (e.g. locals.username)
You have to make sure you're passing username into the Jade template. Example:
app.get('/', function (req, res) {
res.render('index', { title:'APP NAME', username: req.session.username });
});
Also, you would call it like this in the Jade template: #{username}
Related
I want to use res.locals to pass some data between middlewares but typescript says I can't. :(
I am using Restify by the way. Do I need a plugin?
If not, an alternative solution for passing data between middlewares would be appreciated.
⚠️ If you use typescript, then you get an error, because locals is not part of Interface res.
So, if you want to add your own property, for example in res, then you can make it like this: res['locals'] = { name: 'John doe' }.
📤 Update: Add properties to restify res
To declare locals properties to your restifyResponse, than you can use the code below:
declare module 'restify' {
interface Response{
locals: any
}
}
After you declare your own properties, An example locals in your Restify Response, so, now you can use locals in your restifyresponse.
In router/user.js for routing:
router.post('/register', auth.optional, (req, res, next) => {
const {
body: { user }
} = req
// validation code skipped for brevity
const finalUser = new User(user)
finalUser.setPassword(user.password)
// to save the user in Mongo DB
return finalUser.save().then(() => res.json({ user: finalUser.toAuthJSON() }))
})
Post request sent with a body of:
{
"user":{
"email":"leon#idiot.com",
"password": "123abc"
}
}
In model/User.js for database schema:
const UserSchema = new Schema({
email: String,
hash: String,
salt: String
})
// Please note: this is a regular/normal function definition
UserSchema.methods.setPassword = function (password) {
// this references the UserSchema created
this.salt = crypto.randomBytes(16).toString('hex')
console.log(`salt: ${this.salt}`)
this.hash = crypto
.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512')
.toString('hex')
console.log(`hash: ${this.hash}`)
}
Everything works fine now. The log output as:
salt: e7e3151de63fc8a90e3621de4db0f72e
hash: 19dd9fdbc78d0baf20513b3086976208ab0f9eee6d68f3c71c72cd123a06459653c24c11148db03772606c40ba4846e2f9c6d4f1014d329f01d22805fc988f6164fc13d4157394b118d921b9cbd742ab510e4d2fd4ed214a0d523262ae2b2f80f6344fbd948e8c858f95ed9706952db90d415312156a994c65c42921afc8c3e5b1b24a923219445eec8ed62de313ab3d78dc93b715689a552b6449870c5bfcc3bec80c4438b1895cab41f92ef681344ac8578de476a82aa798730cf3a6ef86973a4364a8712c6b3d53ce67ffffd7569b9ade5db09ad95490354c6f7194fdd9d8f8a1cb7ccddf59e701198a1beee59a2dd6afb90ae50e26ea480e9a6d607e4b37857a02016ee4d692d468dd9a67499547eb03fc6cfa676686f7990c2251c9516459288c55584138aed56a5df6c4692f7ef6925e8f3d6f6a0c780c4d80580447f2b1258bea799a8c7eb9da878ab70a94c4227ec03d18d56b2722c315d0e2b2681d81d78d4213288f7305cbbfa377c3b2eb75e0f0b093e6067b14adce4a01f0a7bde8515350a1c987739c12574ec4c49008510e2e7e5534f9b76d15b1af68e43ef54e6b8a1bea859aafd23d6b6bc61d5b1965004cd6dd933545cf755f3e6dfc8f230f37a79a8bc006b9b14465b1b08d60cb45ef3b6a1b73f5afac90bdc58d5ec15c7596dc7e8d503f8dfbd6a3289cf997da2031389c7f3d165e34b29178f3daf76d
3
But it does not work with such a definition:
UserSchema.methods.setPassword = password => {
// what this reference is undefined, so are ones below
this.salt = crypto.randomBytes(16).toString('hex')
console.log(`salt: ${this.salt}`)
this.hash = crypto
.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512')
.toString('hex')
console.log(`hash: ${this.hash}`)
}
The error is:
{
"errors": {
"message": "Cannot set property 'salt' of undefined",
"error": {}
}
}
which means what this referenced is undefined.
What I find online is that fat arrow functions explicitly prevent binding of this, and it's a problem of scopes, that this in fat arrow functions has a scope of its immediate object. But I cannot say that I understand it very well.
1. In this case, what is the scope of this in fat arrow functions?
2. What is the scope, of this, in normal function definitions?
3. How to access the object, in this case: UserSchema, properties (forgive me for less proper words) in fat arrow functions as one does in normal function definitions?
These posts are quite helpful:
Are 'Arrow Functions' and 'Functions' equivalent / exchangeable?
How does the “this” keyword work?
But I am still expecting answers to my specific questions in particular cases, before figuring them out.
The core of your misunderstanding is this:
that this in fat arrow functions has a scope of immediate object
Wrong. It's context is resolved in the scope of the currently executing function/environment.
Examples:
// global scope outside of any function:
let foo = {};
// Define a method in global scope (outside of any function)
foo.a = () => {
console.log(this); // undefined
}
// Return a function from scope of a method:
foo.b = function () {
// remember, "this" in here is "foo"
return () => {
console.log(this); // foo - because we are in scope foo.c()
}
}
foo.a(); // undefined
foo.b()(); // foo
For arrow functions, it is not what object the function belongs to that matters but where it is defined. In the second example, the function can completely not belong to foo but will still print foo regardless:
bar = {};
bar.b = foo.b();
bar.b(); // will log "foo" instead of "bar"
This is the opposite of regular functions which depend on how you call them instead on where you define them:
// Defined in global scope:
function c () {
console.log(this);
}
bar.c = c;
bar.c(); // will log "bar" instead of undefined because of how you call it
Note
Note that there are two very different concepts here that are intermingled - context (what value "this" has) and scope (what variables are visible inside a function). Arrow function uses scope to resolve context. Regular functions do not use scope but instead depend on how you call them.
Questions
Now to answer some of your questions:
In this case, what is the scope of this in fat arrow functions?
As I said. Scope and this are two unrelated concepts. The concept behind this is object/instance context - that is, when a method is called what object is the method acting on. The concept of scope is as simple as what are global variables and what variables exist only inside a specific function and it can evolve to more complicated concepts like closures.
So, since scope is always the same, the only difference is in arrow functions, its context (its this) is defined by the scope. That is, when the function is being declared, where is it declared? At the root of the file? Then it has global scope and this equals "undefined". Inside another function? Then it depends on how that function is called. If it was called as a method of an object such as UserSchema.methods for example if UserSchema.methods.generatePasswordSetter() returns an arrow function, then that function (let's call it setPassword()) will have it's this point to the correct object.
What is the scope, of this, in normal function definitions?
Based on my explanation above I can only sat that scope is not involved with the value of this in normal functions. For a more detailed explanation of how this works see my answer to this other question: How does the "this" keyword in Javascript act within an object literal?
How to access the object, in this case: UserSchema, properties (forgive me for less proper words) in fat arrow functions as one does in normal function definitions?
The way it's defined it is not possible. You need to define it from a regular function that has it's this pointing to UserSchema:
UserSchema.methods.generatePasswordSetter = function () {
return (password) => { /* implementation... */}
}
But this is probably not what you want. To do what you want you only need to stop using arrow functions in this case. Regular functions still exist for use-cases such as this.
So let's say we have the following url:
http://example.com/shops/map/search
I want to access the second segment (map) and check its value.
How can I achieve this in Express? Thanks in advance!
you have to configure your express routes to accept url segments.
app.get('/shops/:type/search', function (req, res) {
res.send(req.params)
})
For a request like this
http://example.com/shops/map/search
req.params will contain required URL segment.
Request URL: http://example.com/shops/map/search
req.params: { "type": "map" }
You can access the url segments by splitting the url into an array.
Like this:
let requestSegments = req.path.split('/');
You can use a route parameters with a constant set of values.
Express uses path-to-regexp to parse the strings you provide for routes. That package permits providing a custom pattern with a parameter to limit the values that can be matched.
app.get('/shops/:kind(list|map)/search', searchShops);
The contents of the parenthesis, (...), are a partial RegExp pattern, in this case equivalent to:
/(?:list|map)/
# within a non-capturing group, match an alternative of either "list" or "map" literally
Then, within searchShops, you can determine which value was given with req.params:
function searchShops(req, res) {
console.log(req.params.kind); // 'list' or 'map'
// ...
}
Alternatively, you can leave the parameter open, checking the value within the handler, and invoke next('route') when the value isn't acceptable:
app.get('/shops/:kind/search', searchShops);
var searchKinds = ['list', 'map'];
function searchShops(req, res, next) {
if (!searchKinds.includes(req.params.kind)) return next('route');
// ...
}
The original answer does the job, but leaves you with an empty element in the array. I'd use the following slight variation to solve this too.
let requestSegments = req.path.split('/').filter((s) => { return s !== ''});
I use the following code to load and save a value in chrome.storage:
$(document).ready(function()
{
$( "#filterPlus" ).click(function()
{
SaveSwitch("plus1","#filterPlus","plus1");
});
}
function SaveSwitch(propertyName, imageId, imageSrc)
{
chrome.storage.sync.get(propertyName, function (result) {
var oldValue = result.propertyName;
alert('GET:old='+oldValue);
if (oldValue==null)
{
oldValue=false;
}
var newValue=!oldValue;
chrome.storage.sync.set({propertyName: newValue}, function()
{
alert('SET:'+newValue);
});
});
}
When I run through this method, the first alert shows:GET:old=undefined, the second alert shows:SET:true just like expected. But when calling that method again with the same parameters the first alert AGAIN shows GET:old=undefined instead of GET:old=true which I expected.
It is the same behaviour when I use storage.local instead of storage.sync
"storage" is in the manifest's permissions. The JS is called from the options-page of my extension-
You're doing .get("plus1", ...) and then later doing .set({"propertyName": newValue}, ...). You are storing under the key "propertyName" but fetching the key "plus1", which has never been set.
Perhaps your misunderstanding is that keys in object literals are themselves literal (even when not quoted), rather than variable identifiers. In that case, you might benefit form reading How to use chrome.storage in a chrome extension using a variable's value as the key name?.
I'm working with Dust.js and Node/Express. Dust.js documents the context helpers functions, where the helper is embedded in the model data as a function. I am adding such a function in my JSON data model at the server, but the JSON response to the browser doesn't have the function property (i.e. from the below model, prop1 and prop2 are returned but the helper property is not.
/* JSON data */
model: {
prop1: "somestring",
prop2: "someotherstring",
helper: function (chunk, context, bodies) {
/* I help, then return a chunk */
}
/* more JSON data */
I see that JSON.stringify (called from response.json()) is removing the function property. Not sure I can avoid using JSON.stringify so will need an alternative method of sharing this helper function between server/client. There probably is a way to add the helper functions to the dust base on both server and client. That's what I'm looking for. Since the Dust docs are sparse, this is not documented. Also, I can't find any code snippets that demonstrate this.
Thanks for any help.
send your helpers in a separate file - define them in a base context in dust like so:
base = dust.makeBase({foo:function(){ code goes here }})
then everytime you call your templates, do something like this:
dust.render("index", base.push({baz: "bar"}), function(err, out) {
console.log(out);
});
what this basically does is it merges your template's context into base, which is like the 'global' context. don't worry too much about mucking up base if you push too much - everytime you push, base recreates a new context with the context you supplied AND the global context - the helpers and whatever variables you defined when you called makeBase.
hope this helps
If you want stringify to preserve functions you can use the following code.
JSON.stringify(model, function (key, value) {
if (typeof(value) === 'function') {
return value.toString();
} else {
return value;
}
});
This probably doesn't do what you want though. You most likely need to redefine the function on the client or use a technology like nowjs.