I've got a findOne() in a template helper in Meteor but I want to modify an array in the document before returning both the document as well as the updated array. When doing so I get TypeError: Cannot read property 'access' of undefined. On the initial test it worked fine but I suspect that it's now failing because I'm trying to modify the document before findOne() completes. How do I get around this? Code below:
'curMatter': function() {
var curObj = Matters.findOne({_id:Session.get('editing_matter')});
var curAccess = _.without(curObj.access, Meteor.userId());
return { curMatter: curMatter, curAccess: curAccess };
}
Collection.findOne completes before running code code that is after it (var curAccess = _.without(curObj.access, Meteor.userId()); in your case).
The issue is that the document that you expect to get by this query is not available (yet) to the client, so Matters.findOne({_id:Session.get('editing_matter')}); returns undefined.
So what it really means that your subscription is not ready, when the helper is run for the first time, and it fails.
What you can do is to check whether curObj is not undefined before accessing its property (i.e. var curAccess = curObj && _.without(curObj.access, Meteor.userId());
When the document becomes available to the client, the helper will be re-run, and you will get correct results then.
Here is your complete code:
'curMatter': function() {
var curObj = Matters.findOne({_id:Session.get('editing_matter')});
var curAccess = curObj && _.without(curObj.access, Meteor.userId());
return { curMatter: curMatter, curAccess: curAccess };
}
just use it as a cursor, they always seem to work better anyway.
Template['someTemplate'].helpers({
curMatter: function () {
return Matters.find({
_id: Session.get('editing_matter')
}).map(doc => Object.assign(doc, {
curAccess: _.without(doc.access, Meteor.userId())
}))
}
});
Then you can use an {{#each}} in your template (even if it is always one item)
Related
There's a troncated code of a page Object in protractor
that code is working :
var HomePage = function() {
this.publishedShows = element.all(by.repeater('show in showsHomePage'));
this.getFirstShow = function(){
return this.publishedShows.first();
}
};
this one is not :
var HomePage = function() {
this.publishedShows = element.all(by.repeater('show in showsHomePage'));
this.getFirstShow = function(){
return this.publishedShows.get(0);
}
};
I get this error :
Index out of bound. Trying to access element at index: 0, but there are only 0 elements that match locator by.repeater("show in showsHomePage")
Anyone can inlight me?
It is not about get(0) vs first() - they are absolutely the same in terms of implementation. It is probably about the timing, wait for the presence of the element before making any action with it:
var elm = myPageObject.getFirstShow();
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(elm), 5000);
// do smth with elm
alecxe does have a point about waiting for the element to be present and so you may want to the wait as mentioned or browser.waitForAngular();
What I have seen is that if you resolve a finder to a variable then this can get left in the unfulfilled promise state (even though the internals have resolved the query). What needs to be done is to resolve the promise and then you should be able to get the element you require:
So from your code:
`this.publishedShows = element.all(by.repeater('show in showsHomePage'));`
Will still be a promise and not publishedShows.
This returns items when I try your code (I have a slightly different repeater).
var HomePage = function() {
this.publishedShows = element.all(by.repeater('show in showsHomePage'));
this.getFirstShow = function() {
return this.publishedShows.then(function(items){
=>return items[0].getText();
});
}
};
var hp = new HomePage();
=>expect(hp.getFirstShow()).toEqual('hello');
Obviously change your expect to what you want to check for and also the return too. Marked with =>
Ensure also that if you use any track by statement then you should look at the by.exactRepeater command to have an exact match on only the repeater part.
This worked for me, note the resolved promise returns an array of finders.
When user refresh a certain page, I want to set some initial values from the mongoDB database.
I tried using the onRendered method, which in the documentation states will run when the template that it is run on is inserted into the DOM. However, the database is not available at that instance?
When I try to access the database from the function:
Template.scienceMC.onRendered(function() {
var currentRad = radiationCollection.find().fetch()[0].rad;
}
I get the following error messages:
Exception from Tracker afterFlush function:
TypeError: Cannot read property 'rad' of undefined
However, when I run the line radiationCollection.find().fetch()[0].rad; in the console I can access the value?
How can I make sure that the copy of the mongoDB is available?
The best way for me was to use the waitOn function in the router. Thanks to #David Weldon for the tip.
Router.route('/templateName', {
waitOn: function () {
return Meteor.subscribe('collectionName');
},
action: function () {
// render all templates and regions for this route
this.render();
}
});
You need to setup a proper publication (it seems you did) and subscribe in the route parameters. If you want to make sure that you effectively have your data in the onRendered function, you need to add an extra step.
Here is an example of how to make it in your route definition:
this.templateController = RouteController.extend({
template: "YourTemplate",
action: function() {
if(this.isReady()) { this.render(); } else { this.render("yourTemplate"); this.render("loading");}
/*ACTION_FUNCTION*/
},
isReady: function() {
var subs = [
Meteor.subscribe("yoursubscription1"),
Meteor.subscribe("yoursubscription2")
];
var ready = true;
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
data: function() {
return {
params: this.params || {}, //if you have params
yourData: radiationCollection.find()
};
}
});
In this example you get,in the onRendered function, your data both using this.data.yourData or radiationCollection.find()
EDIT: as #David Weldon stated in comment, you could also use an easier alternative: waitOn
I can't see your collection, so I can't guarantee that rad is a key in your collection, that said I believe your problem is that you collection isn't available yet. As #David Weldon says, you need to guard or wait on your subscription to be available (remember it has to load).
What I do in ironrouter is this:
data:function(){
var currentRad = radiationCollection.find().fetch()[0].rad;
if (typeof currentRad != 'undefined') {
// if typeof currentRad is not undefined
return currentRad;
}
}
I'm trying to return window.performance object from the web page back to casper's scope with the following code but I'm getting null. Can someone explain why?
performance = casper.evaluate ->
return window.performance
#echo performance
PhantomJS 1.x doesn't implement window.performance, so you can't use it.
PhantomJS 2.0.0 implements it, but it doesn't implement the window.performance.toJSON() function. The problem with PhantomJS is that you have to access this information through evaluate(), but it has the following limitation:
Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine.
Closures, functions, DOM nodes, etc. will not work!
You will have to find your own way of serializing this in the page context and passing it to the outside (JavaScript):
var performance = casper.evaluate(function(){
var t = window.performance.timing;
var n = window.performance.navigation;
return {
timing: {
connectStart: t.connectStart,
connectEnd: t.connectEnd,
...
},
navigation: {
type: n.type,
redirectCount: n.redirectCount
},
...
};
});
or look for a deep copy algorithm that produces a serializable object (from here):
var perf = casper.evaluate(function(){
function cloneObject(obj) {
var clone = {};
for(var i in obj) {
if(typeof(obj[i])=="object" && obj[i] != null)
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
return cloneObject(window.performance);
});
console.log(JSON.stringify(perf, undefined, 4));
i'm working on a Meteor project, and I must say that isn't easy at all, especially for one thing: callbacks !
Everything is async, so I wonder how do I must do to get results from my mongodb.
var user = Meteor.users.findOne({username: "john"});
return (user); // sometimes returns "undefined"
...
var user = Meteor.users.findOne({username: "john"});
if (user) // so ok, I check if it exists!
return (user); // Cool, I got my user!
return (); // Ok and what should I return here? I want my user!
I don't want to be dirty and put like setTimeout everywhere.
Anybody has a solution for this ?
EDIT :
I noticed in router.js with console.log that my data is returned 4 times. 2 times with an undefined value and 2 other times with the expected value. In the view, it's still undefined.
Why the router passes like 4 times in this route ? Does it display the first result of the return value in the router ?
What should I return if the find() doesn't find anything ?
EDIT 2: Here is some code to understand.
this.route('profilePage', {
path: 'profil/:_id?',
waitOn: function() {
return [
Meteor.subscribe('article', { prop: this.params._id}), // id can be id or username
Meteor.subscribe('article', { userId: this.params._id}), // id can be id or username
Meteor.subscribe('params'),
Meteor.subscribe('profil', (this.params._id ? this.params._id : Meteor.userId()))
];
},
data: function() {
if (this.params._id) {
var user = Meteor.users.findOne(this.params._id);
if (!user)
user = Meteor.users.findOne({username: this.params._id});
console.log(user);
return user;
}
else if (Meteor.userId())
return Meteor.user();
else
Router.go("userCreate");
}
});
I get this on the console:
http://puu.sh/debdJ/69419911f7.png
(text version following)
undefined
undefined
Object_id: "o3mgLcechYTtHPELh"addresses: (....)
Object_id: "o3mgLcechYTtHPELh"addresses: (....)
findOne(yourId) is a sync method which is equivalent to find({ _id: yourId}, callback). The difference is that find() allows you to define a callback. If you don't pass a callback to find() this method will be sync.
check wrapAsync: http://docs.meteor.com/#/full/meteor_wrapasync
It allows you to code in a sync style with a async operations.
Free lesson on EventedMind: https://www.eventedmind.com/feed/meteor-meteor-wrapasync
My experience thus far is that the Meteor Mongodb package is that the functions do not generally provide callbacks (for some reason insert does...), the functions are atomic (thus sync).
There are meteor packages that can make Mongodb async if you want (I havn't tried any).
I guess this sync approach is in line with the simple maintenance goal of Mongodb. Thinking about it, one of my pet peeves using Node is working with async callback waterfalls/nests, they are a pain to create and maintain... and hopefully this will make my code easier to read and understand and change...
var future = new Future();
var _h = Hunts.findOne({huntId});
if(_h) {
future.return(_h)
} else {
return future.wait();
}
on server/startup.js you need:
Future = Npm.require('fibers/future');
I've tried to understand this post regarding this concept, however, I'm failing to get it. I have the following simple setup:
/server/test.js
Meteor.methods({
abc: function() {
var result = {};
result.foo = "Hello ";
result.bar = "World!";
return result;
}
});
/client/myapp.js
var q = Meteor.call('abc');
console.log(q);
This structure returns to the console undefined.
If I change the myapp.js file to:
Meteor.call('abc', function(err, data) {
!err ? console.log(data) : console.log(err);
}
I receive the Object in my console.
Ideally this is what I'd like to be able to do, but it doesn't work, stating in the console: Cannot read property 'greeting' of undefined
/client/myapp.js
var q = Meteor.call('abc');
Template.hello.greeting = function() {
return q.foo;
}
Any help in passing the data from the server object into the template would be greatly appreciated. I'm still learning JavaScript & Meteor.
Thanks!
From the Meteor.call documentation:
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
So, you'll want to do it like this:
Meteor.call('abc', function(err, data) {
if (err)
console.log(err);
Session.set('q', data);
});
Template.hello.greeting = function() {
return Session.get('q').foo;
};
This will reactively update the template once the data is available.
This happens because Npm.require has Async behavior. That's the reason that you have to write a callback for Meteor.call.
But there is a solution, just use install(mrt add npm) and you'll get a function named Meteor.sync(//...) with this you can do both games: sync and async in your Meteor.call().
Reference: http://www.sitepoint.com/create-a-meteor-app-using-npm-module/
You can get the return value of a Meteor method for use in a template by using a reactive variable. Check out the working demonstration on Meteorpad
I went for a ghetto solution. But, it works for me, which is what matters, to me. Below is my code, which, in concept, I think, solves OP's problem.
In the client's main.js:
Meteor.setInterval(function() {
confirmLogin();
}, 5000);
This runs the confirmLogin() function every five seconds.
The confirmLogin function (in the client's main.js):
function confirmLogin() {
Meteor.call('loggedIn', function (error, result) {
Session.set("loggedIn", result);
});
}
The loggedIn method (in the server's main.js):
loggedIn: function () {
var toReturn = false;
var userDetails = Meteor.user();
if (typeof userDetails["services"] !== "undefined") {
if (typeof userDetails["services"]["facebook"] != "undefined") {
toReturn = true;
}
}
return toReturn;
},
The relevant helper:
loggedIn: function () {
return Session.get("loggedIn");
}