I have the following controller method in a AngularJS application using RestAngular:
$scope.findFriend = function (name, type, limit) {
return FriendSearch.getList({
name: name,
type: type,
limit: limit
});
};
It might be that the type is empty (a string with no content, length = 0) - Restangular generates the following invalid URL:
http://host:port/rest/friends/search?limit=10&name=Peter&type=
Correct would be to omit it like:
http://host:port/rest/friends/search?limit=10&name=Peter
Of course, I can check the parameters before passing. But I would like to know if there is a nicer way to do that.
Unfortunately I cannot reproduce this issue with Restangular version 1.4. I have created a fiddle resembling your example:
http://plnkr.co/edit/ovwd8wUhBkhPXtBNDfbw?p=preview
Excerpt:
// http://run.plnkr.co/search?name=foo
$scope.findFriend('foo');
// http://run.plnkr.co/search?name=foo&type=bar
$scope.findFriend('foo', 'bar');
// http://run.plnkr.co/search?limit=3&name=foo
$scope.findFriend('foo', null, '3');
// http://run.plnkr.co/search?limit=3&name=foo
$scope.findFriend('foo', undefined, '3');
Both null and undefined given as parameters work as intended.
This means we need some more information / code because the behaviour might be caused by something else.
For the case of empty strings the most concise and easy way is to simply check for '' and replace with null.
I updated the fiddle accordingly:
$scope.findFriend = function (name, type, limit) {
return FriendSearch.getList({
name: name === '' ? null : name,
type: type === '' ? null : type,
limit: limit === '' ? null : limit
});
};
// http://run.plnkr.co/search?limit=3&name=foo
$scope.findFriend('foo', '', '3');
Related
I came through lately following line of code while analyzing 3rd party data script.
CREATE OR REPLACE PROCEDURE WH.SCHEMA.PROCEDURE_NAME(DATE_OF_LOAD STRING) --input which will be binded later
RETURNS STRING
LANGUAGE javascript
AS $$
var drop_table = `drop table if exists TABLE_NAME;`;
var stmt_drop_table = snowflake.createStatement( {sqlText: drop_table} );
var incremental_data =
`CREATE TABLE AS <many arguments>
WHERE P.CZAS_MODYF_SF >= :1 --this is where biding of DATE_OF_LOAD occurs)
SELECT *, HASH(*) HASH FROM (
SELECT <arguments>
FROM CTE) q; `;
var stmt_incremental_data = snowflake.createStatement( {sqlText: incremental_data,binds: [DATE_OF_LOAD ].map(function(x){return x === undefined ? null : x}) } );
try {
stmt_drop_table.execute();
stmt_incremental_data.execute();
rtr = "Success!";
return rtr;
}
catch (err) {
return "Failed: " + err;
}
$$
;
Entire challenge I have is with:
var stmt_incremental_data = snowflake.createStatement( {sqlText: incremental_data,binds: [DATE_OF_LOAD ].map(function(x){return x === undefined ? null : x}) } ).
object.method part is clear. Same for binds. Code after binds is my issue here.
Another topic: Snowflake interpretes method's parameters as a JSON. Does it mean that bind value can be extended by assigning JS code?
I'll be greatly thankful for help and explanation.
The reason I did not post this as an answer initially is that JavaScript is not my area of expertise. I did dabble in it for a few months.
But in order to understand what is going on with:
[DATE_OF_LOAD ].map(function(x){return x === undefined ? null : x})
You need to break it down:
x === undefined ? null : x
This is called an elvis operator and is the equivalent of :
if (x === undefined)
{
return null
} else {
return x
}
Now that we know what the function does, we need to understand the map method. But in short it creates a new array with the results of calling a provided function on every element in the calling array.
So the simple answer copied from my comment is:
if DATE_OF_LOAD is undefined it will replace it with null, otherwise it will use whatever value is stored in DATE_OF_LOAD. That is because SQL does not know how to handle undefined.
But here is the reasoning for my answer.
Is there something that I'm missing that would allow item to log as an object with a parameter, but when I try to access that parameter, it's undefined?
What I've tried so far:
console.log(item) => { title: "foo", content: "bar" } , that's fine
console.log(typeof item) => object
console.log(item.title) => "undefined"
I'll include some of the context just in case it's relevant to the problem.
var TextController = function(myCollection) {
this.myCollection = myCollection
}
TextController.prototype.list = function(req, res, next) {
this.myCollection.find({}).exec(function(err, doc) {
var set = new Set([])
doc.forEach(function(item) {
console.log(item) // Here item shows the parameter
console.log(item.title) // "undefined"
set.add(item.title)
})
res.json(set.get());
})
}
Based on suggestion I dropped debugger before this line to check what item actually is via the node repl debugger. This is what I found : http://hastebin.com/qatireweni.sm
From this I tried console.log(item._doc.title) and it works just fine.. So, this seems more like a mongoose question now than anything.
There are questions similar to this, but they seem to be related to 'this' accessing of objects or they're trying to get the object outside the scope of the function. In this case, I don't think I'm doing either of those, but inform me if I'm wrong. Thanks
Solution
You can call the toObject method in order to access the fields. For example:
var itemObject = item.toObject();
console.log(itemObject.title); // "foo"
Why
As you point out that the real fields are stored in the _doc field of the document.
But why console.log(item) => { title: "foo", content: "bar" }?
From the source code of mongoose(document.js), we can find that the toString method of Document call the toObject method. So console.log will show fields 'correctly'. The source code is shown below:
var inspect = require('util').inspect;
...
/**
* Helper for console.log
*
* #api public
*/
Document.prototype.inspect = function(options) {
var isPOJO = options &&
utils.getFunctionName(options.constructor) === 'Object';
var opts;
if (isPOJO) {
opts = options;
} else if (this.schema.options.toObject) {
opts = clone(this.schema.options.toObject);
} else {
opts = {};
}
opts.minimize = false;
opts.retainKeyOrder = true;
return this.toObject(opts);
};
/**
* Helper for console.log
*
* #api public
* #method toString
*/
Document.prototype.toString = function() {
return inspect(this.inspect());
};
Make sure that you have defined title in your schema:
var MyCollectionSchema = new mongoose.Schema({
_id: String,
title: String
});
Try performing a for in loop over item and see if you can access values.
for (var k in item) {
console.log(item[k]);
}
If it works, it would mean your keys have some non-printable characters or something like this.
From what you said in the comments, it looks like somehow item is an instance of a String primitive wrapper.
E.g.
var s = new String('test');
typeof s; //object
s instanceof String; //true
To verify this theory, try this:
eval('(' + item + ')').title;
It could also be that item is an object that has a toString method that displays what you see.
EDIT: To identify these issues quickly, you can use console.dir instead of console.log, since it display an interactive list of the object properties. You can also but a breakpoint and add a watch.
Use findOne() instead of find().
The find() method returns an array of values, even if you have only one possible result, you'll need to use item[0] to get it.
The findOne method returns one object or none, then you'll be able to access its properties with no issues.
Old question, but since I had a problem with this too, I'll answer it.
This probably happened because you're using find() instead of findOne(). So in the end, you're calling a method for an array of documents instead of a document, resulting in finding an array and not a single document. Using findOne() will let you get access the object normally.
A better way to tackle an issue like this is using doc.toObject() like this
doc.toObject({ getters: true })
other options include:
getters: apply all getters (path and virtual getters)
virtuals: apply virtual getters (can override getters option)
minimize: remove empty objects (defaults to true)
transform: a transform function to apply to the resulting document before returning
depopulate: depopulate any populated paths, replacing them with their original refs (defaults to false)
versionKey: whether to include the version key (defaults to true)
so for example you can say
Model.findOne().exec((err, doc) => {
if (!err) {
doc.toObject({ getters: true })
console.log('doc _id:', doc._id) // or title
}
})
and now it will work
You don't have whitespace or funny characters in ' title', do you? They can be defined if you've quoted identifiers into the object/map definition. For example:
var problem = {
' title': 'Foo',
'content': 'Bar'
};
That might cause console.log(item) to display similar to what you're expecting, but cause your undefined problem when you access the title property without it's preceding space.
I think using 'find' method returns an array of Documents.I tried this and I was able to print the title
for (var i = 0; i < doc.length; i++) {
console.log("iteration " + i);
console.log('ID:' + docs[i]._id);
console.log(docs[i].title);
}
If you only want to get the info without all mongoose benefits, save i.e., you can use .lean() in your query. It will get your info quicker and you'll can use it as an object directly.
https://mongoosejs.com/docs/api.html#query_Query-lean
As says in docs, this is the best to read-only scenarios.
Are you initializing your object?
function MyObject()
{
this.Title = "";
this.Content = "";
}
var myo1 = new MyObject();
If you do not initialize or have not set a title. You will get undefined.
When you make tue query, use .lean() E.g
const order = await Order.findId("84578437").lean()
find returns an array of object , so to access element use indexing, like
doc[0].title
I want so set a boolean value I either get from a property in an options object, or if this is not defined want to set a default value.
const raw = options.rawOutput;
If options.rawOutput is not set, the default value should be true.
Problem is: the options object may not exist at all.
Im looking for a more elegant solutions than something like this
if (options) {
if (options.rawOutput) {
raw = rawOutput;
}
} else {
raw = true;
}
You could check if options exists and if the property rawOutput exists, then take that value, otherwise take true.
raw = options && 'rawOutput' in options ? options.rawOutput : true;
or the same but without conditional (ternary) operator ?:.
raw = !options || !('rawOutput' in options) || options.rawOutput;
I would like to use typeof check:
const raw = ((typeof options == 'undefined') || (typeof options.rawOutput == 'undefined'))? true:options.rawOutput;
Using the power of ES6:
const { rawOptions: raw = true } = options || {};
using object deseructuring to get the rawOptions and assigning it to raw variable and with a default value of true
Try this in case you want to assign false if options is exists but rawOutput is not.
const raw = options ? (options.rawOutput ? rawOutput : false) : true;
You can do this using just logical operators,
const raw = options && options.rawOutput || true;
This would set raw to true in case either of options or options.rawOutput is falsy.
When we discuss about options, nowadays the approach is having always the object defined, to defaults at least. So, for example, in your case, you will have:
// list of all defaults value for options
const defaultOptions = {
rawOutput: true
}
// assuming you are in a function when you get the options
function doSomething(userOptions) {
// here you will have all the options, with `userOptions`
// overrides the default options if they're defined.
const options = {...defaultOptions, ...userOptions};
if (options.rawOutput) {
// do stuff
}
}
This is helpful especially when you have more than one options to pass, and you could have defaults for most of them. In this way, you don't have to check if any object or properties exists every time, and you also have a clear list of the options' default that you can change – or get from a JSON – without impact your code.
I'm using the immutable.js and redux in project, and I found an quite strange issue.
here is the code used in selector:
{
dealDetail : dealDetails.get(id.toString()).toJS(),
dealTrackLog : dealTrackLogs.get(id).toJS()
}
First, the id is Number, in detail, I must pass string of id, and in trackLogs, on the contrary, it must be Number, otherwise will cause error, "cannot read property toJS() of undefined"
and I think the problem maybe in reducer, here is the code:
// dealDetailReducer
// const initialStateOfDealDetail = fromJS({})
let details = {}
action.data.details.map((detail) => {
details[detail.id] = detail
})
return state.merge(fromJS(details))
...
// dealTrackLogsReducer
// initialStateOfDealTrackLogs = fromJS({})
if (state.get(action.data.id)) {
// has id in state, update
return state.withMutations(s =>
s.update(
action.data.id,
trackLog => trackLog.merge(fromJS(action.data.trackLogs))
)
)
}
// no id in state, just set, id : data
return state.set(action.data.id, fromJS(action.data)
so, I'm hard to understand why and when to pass a Number/String ?
First line
let details = {}
You are using regular object for details state. Objects coerce to string keys.
The second case you are using immutablejs operation that preserve the key type.
Here is the html:
<select style="width: 100%;" ng-model="vm.orgType" ng-model-options="{getterSetter: true}" ng-options="orgType as orgType.ORGANIZATION_TYPE for orgType in vm.orgTypes">
</select>
and here is the getter/setter function:
function orgType(selectedType) {
if (arguments.length == 0)
return orgType.selectedOrgType || { ORGANIZATION_TYPE: 'Organization Type', ORGANIZATION_TYPE_ID: null };
orgType.selectedOrgType = selectedType;
if (selectedType.ORGANIZATION_TYPE_ID) {
if (vm.registrant.StakeholderOrgs[0])
vm.registrant.StakeholderOrgs[0] = selectedType.ORGANIZATION_TYPE_ID;
else
vm.registrant.StakeholderOrgs.push(selectedType.ORGANIZATION_TYPE_ID);
}
else
vm.registrant.StakeholderOrgs.splice(0);
}
the following line:
return orgType.selectedOrgType || { ORGANIZATION_TYPE: 'Organization Type', ORGANIZATION_TYPE_ID: null };
throws the infinite digest loop error.
Let me explain what I am trying to do here. I need to push the id onto a list if there is a selection made. I realize that I could just do an ng-model on some variable selectedOrgType and then just put my logic in an ng-change. However, I am trying to make a dropdown that does not create any unnecessary model variables. Instead, I was hoping to just put the logic in a getter/setter, that seems more appropriate to me. One of vm.orgTypes is { ORGANIZATION_TYPE: 'Organization Type', ORGANIZATION_TYPE_ID: null }, I was hoping that would be my default value instead I get this digest error, don't understand where it is coming from.
When you add ng-model attribute, angular add internal watch, that check value on every digest loop, and if value changed - run digest again.
In you case you return object literal. In javascript when you compare two literals, even with same structure - you get false
({a:1} == {a:1}) // false
because this really two different object.
So, when you return object literal in your getter, watch check it with previous value, and, as i say above, if you return literal - get false
So you get your error with infinite digest.
For solving you just need return same object.
If you have this object inside array, like
vm.orgTypes=[
{ ORGANIZATION_TYPE: 'Organization Type', ORGANIZATION_TYPE_ID: null }
];
So you just need use it directly:
return orgType.selectedOrgType || orgTypes[0];
Yer another way: just save default falue to varible and use it
var defaultSelect = { ORGANIZATION_TYPE: 'Organization Type', ORGANIZATION_TYPE_ID: null };
....
function orgType(selectedType) {
if (arguments.length == 0)
return orgType.selectedOrgType || defaultSelect;
In this case you would return same element in default case, so avoid infinite digest loot.