Reputation: 3402
Can someone explain to me why when I have collections code inside router will cause the route to be called when a method is called?
Consider the following code:
home.html
<template name="home">
{{ duplicate }}
<form>
<input type="text" name="test" value="somevalue">
<input type="submit" value="Submit">
</form>
</template>
script.js
Template.home.events({
'submit form': function (e) {
e.preventDefault();
console.log('Enter Meteor call');
Meteor.call('createDoc', { 'test': e.target.test.value });
}
});
route.js
Router.onBeforeAction(function () {
console.log('Enter onBeforeAction');
$('#loading').show();
this.next();
});
Router.route('/', function () {
console.log('Enter action');
var foo = collection.findOne({ test: 'somevalue' }) ? 'true' : 'false';
this.render('home', {
data: {
'duplicate' : foo
}
});
Template.home.rendered = function () {
console.log('Enter rendered');
$('#loading').hide();
};
});
methods.js
collection = new Mongo.Collection('collection');
Meteor.methods({
createDoc: function (data) {
console.log('Enter createDoc');
collection.insert(data);
}
});
The problem is that if I press submit on the form, after the method is called the router will activate, even though e.preventDefault()
presents. The console log shows this behaviour clearly:
"Enter Meteor call" script.js:4:3
"Enter createDoc" methods.js:5:3
"Enter onBeforeAction" routes.js:2:2
"Enter action" routes.js:8:2
"Enter onBeforeAction" routes.js:2:2
"Enter action" routes.js:8:2
Furthermore, you can see that the router is called twice and that it never enters Template.home.rendered
. This causes the loading div to appear and never leaves. I can confirm that data are being inserted correctly.
If I remove collection.findOne()
in routes.js, however, this behaviour will disappear and everything works as expected.
Why is the route being called only when I have collection.findOne()
inside the route?
Why collection.findOne({ test: 'somevalue' })
never returns anything inside the route? (I know how I can get around this by using Session variables and helpers in script.js, but I want to know exactly why)
This is causing a lot of unexpected behaviour in my app. Thank you very much in advance.
Upvotes: 3
Views: 434
Reputation: 4019
As answered by others the problem you have arises from the fact that Meteor will reactively re-run code that runs in a reactive context, if and only if, that code issues a call to a reactive data source.
In your case, the call to findOne
is a call to a reactive data source and the context in Router.route('/', function () { // context });
is a reactive context.
There are two important tools that let you control this behavior: one is good design. Be aware of the reactivity and try to design your code around it. The other is checking Tracker.active and using Tracker.nonreactive to avoid reactivity inside a reactive data context.
This should answer your first question. As to why your findOne
query never finds anything: have you published the data from the server to the client? Please check out Publish-Subscribe. You basically need:
// on the server
Meteor.publish('myPublication', function(author) {
return collection.find();
});
// on the client
Meteor.subscribe('myPublication');
Upvotes: 2
Reputation: 973
The call to collection.findOne() inside the route is listening to any new changes on the database, every time text is saved on the database the query is run.
A possible solution: Router.js
Router.onBeforeAction(function () {
console.log('Enter onBeforeAction');
$('#loading').show();
this.next();
});
Router.route('/', {
template: 'home',
waitOn: function() {
return Meteor.subscribe('collection');
},
data: function() {
var foo = collection.findOne({ test: 'somevalue' }) ? 'true' : 'false';
return {
'duplicate': foo
};
},
action: function() {
this.render();
}
});
And a publish file on server/publish.js
Meteor.publish('collection', function () {
return collection.find();
});
I hope this can help you solving your problem. Best.
Upvotes: 0