Using this code in a file "reports.js" in the same folder of "reports.html", I get 0 elements in the array returned. Not sure I am missing any extra declaration but also I can't use "import { Mongo } from 'meteor/mongo';" without having error "Use of reserved word 'import'".
Meteor version 1.2.1
JS
Tasks = new Mongo.Collection('tasks');
if(Meteor.isClient){
Template.reports.rendered = function(){
if(Session.get("animateChild")){
$(".reports-page").addClass("ng-enter");
setTimeout(function(){
$(".reports-page").addClass("ng-enter-active");
}, 300);
setTimeout(function(){
$(".reports-page").removeClass("ng-enter");
$(".reports-page").removeClass("ng-enter-active");
}, 600);
}
};
}
Template.dashboardd.helpers({
options() {
return Tasks.find({});
},
});
HTML
<Template name="dashboardd">
{{#each options}}
<p><label>{{text}}</label></p>
{{/each}}
</Template>
AFAIK meteor 1.2 don't supports Imports. Improrts are new module added i meteor 1.3 or 1.4 im not sure.
I allways use this 3 Elements when work with collections (4 if we take collection declaration)
You should do on server side some publish and allows like:
Meteor.publish("Tasks ", function (selector, options) {
//you can remove condition if you dont have accounts-package
if (this.userId) {
return Tasks .find(selector || {}, options || {});
}
});
And some Allows
Once again if you dont have Meteor-accounts you can simply - return true, on inserts, updates etc. But I don't must say that is not safety :P
Tasks.allow({
insert: function (userId, doc) {
return userId;
},
update: function (userId, doc) {
return userId;
},
remove: function (userId) {
return userId;
},
});
On Client side some subscribe
Template.YourTemplateName.onCreated(function () {
this.autorun(() => {
this.subscribe('Tasks', { _id: Template.currentData()._id });
//or
this.subscribe('Tasks');
//but this solution can kill Browser when you collection will have a thousand elements. Allways try to not subscribe all collection. Only Needed fields/objects
});
});
Related
I am newbie to meteor. I had established the publish / subscribe concept. I am facing the following error while performing aggregation reactively.
client code :
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
Template.header.helpers({
'tasks': function () {
console.log("tasks helper called : ");
Meteor.subscribe('reportTotals', function() {
console.log(clientReport.find().fetch());
});
return ['1', '2'];
},
});
server code
import { Meteor } from 'meteor/meteor';
import {ReactiveAggregate} from 'meteor/jcbernack:reactive-aggregate';
Meteor.startup(() => {
console.log("Server Started");
// code to run on server at startup
var MONGO_URL = "mongodb://127.0.0.1:27017/test";
});
Meteor.publish("reportTotals", function() {
// Remember, ReactiveAggregate doesn't return anything
this.autorun(function () {
ReactiveAggregate(this, atm_data, [{
// assuming our Reports collection have the fields: hours, books
$group: {
'_id': null,
'bottles_used': {
// In this case, we're running summation.
$sum: '$BOTTLE_USED'
// $sum: 1
}
}
}, {
$project: {
// an id can be added here, but when omitted,
// it is created automatically on the fly for you
bottles_used: '$bottles_used'
} // Send the aggregation to the 'clientReport' collection available for client use
}], { clientCollection: "clientReport" });
});
});
Exception in flushing DDP buffered writes: Error: Expected to find a document to change
at Object.update (http://localhost:3000/packages/mongo.js?hash=ed0b13aca2f180af120dd0cfdba64ac79e2a624f:246:29)
...
Thanks in advance.
You don't have a client side collection. Also, you need to subscribe before you call this helper.
Try this
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
var clientReport = new Mongo.Collection('clientReport');
Meteor.subscribe("reportTotals");
Template.header.helpers({
'tasks': function () {
console.log("tasks helper called : ");
console.log(clientReport.find().fetch());
},
});
You also don't need the pipeline and no autorun on server code, try this:
AtmData = new Mongo.Collection('atmdata');
Meteor.startup(() => {
// code to run on server at startup
/* AtmData.insert({
bottles_used: 123,
}); */
});
Meteor.publish("reportTotals", function() {
// Remember, ReactiveAggregate doesn't return anything
ReactiveAggregate(this, AtmData, [{
// assuming our Reports collection have the fields: hours, books
$group: {
'_id': null,
'bottles_used': {
// In this case, we're running summation.
$sum: '$bottles_used'
// $sum: 1
}
}
}], { clientCollection: "clientReport" });
});
I made an NPM package here: meteor-publish-join. Its main purpose is to publish expensive-aggregated values after a certain amount of time. Hope it will help in your case.
I have a sample code that goes like this:
Client Helper:
getUsername: function (userId) {
Meteor.call("getUsername", userId, function (err, result) {
if(!err) {
Session.set("setUsername", result);
else {
console.log(err);
}
});
return Session.get("setUsername");
}
Server
Meteor.methods({
"getUsername": function (userId) {
var x = Meteor.users.find({_id: userId}, {fields: {username:1}}).fetch()[0];
return x.username;
}
});
The result of this code is an infinite loop of username passing to the client. Is there a way to stop the loop and pass only the data that is needed on the client? I believe the reactivity is causing the data to loop infinitely and I am not sure how to stop it. I tried using "reactive":false on my query in the server but it does not work.
If you want to access username everywhere in client templates (so thats why you put it into session), I would not set it in template helper. I would set it on startup and get username from session in template helpers (without calling server method)
If you need username just in one template, so you want to return its value from your template helper, do not put it into session, just return it in your server method callback.
Based on your sample code, I assume, you have a set of posts and you are retrieving user name based on user id for each post. Then instead of doing it this way, you should use publish composite package to publish related users as well.
Meteor.publishComposite('getPosts', function (postIds) {
return [{
find: function() {
return Posts.find({ _id: { $in: postIds }});
// you can also do -> return Posts.find();
// or -> return Posts.find({ /* or what ever your selector is to get the posts you need*/ });
},
children: [{
find: function(post) {
return Meteor.users.find({
id: post.userId //or the correct field in your post document to get user id
}, {
fields: {
"profile": 1
}
});
}
}}
}]
});
This way your publication will take care of publishing related users along with posts. You don't need to use methods and call them each time.
I'm using Meteor 1.0
This probably is a common beginners mistake but I can't seem so find the solution.
When I ask the server to return a collection, the results are not shown. However, I console log shows me the good results.
Here is the client helper:
Template.myProjectTips.helpers({
matchingProjects: function() {
Meteor.call('searchNearProjects', function(error, result) {
if (error) {
console.log(error.reason);
return;
} else {
var fields = [{}];
_.each(result, function(field) {
fields = field;
console.log('result: ' + fields.title);
});
return fields;
}
});
},
});
Can someone help me out with it?
First of all, your method should return a cursor, so you should remove .fetch() which in fact traverses the cursor to return an array. Cursor is natively supported and promoted in Meteor.
Meteor.methods({
searchNearProjects: function() {
return Project.find({}, {sort: {createdAt: -1}, limit: 10});
}
});
Also in your template code, you are replacing your array, instead you should be populating it. But since you are working with a live cursor, you don't need all of that _.each() traversal either. Therefore you can simply do:
Template.myProjectTips.helpers({
matchingProjects: function() {
Meteor.call('searchNearProjects', function(error, result) {
if (error) {
console.log(error.reason);
return;
} else {
return result;
}
});
}
});
But for a very simple Mongo query like this, you should not be using a Meteor.method
Instead you should publishing and subscribing like this:
Server:
Meteor.publish("nearProjects", function () {
return Project.find({}, {sort: {createdAt: -1}, limit: 10});
});
Client:
Meteor.subscribe("nearProjects");
Template.myProjectTips.helpers({
matchingProjects: function() {
return Project.find({}, {sort: {createdAt: -1}, limit: 10});
}
});
Or if you want to publish with some find criteria, you can pass in your arguments to your subscribe and publish functions as well.
Im new to meteor and have been trying to learn the framework via the discover meteor book. Im having a few issue understanding what exactly is going on in my application (found here https://github.com/Themitchell/Sound-wav.es).
Essentially, my understanding is that on my server side I allow publications for certain collections which take arguments from my client side subscribe calls. For this part on my server i have this in my server/publications.js file:
Meteor.publish('studios', function (options) {
return Studios.find({
userId: this.userId
}, options);
});
Meteor.publish('studio', function (id) {
return id && Studios.find({
userId: this.userId,
_id: id
});
});
Next we would need a controller to handle the routing and deal with waiting for any subscriptions required, then, once the subscriptions are ready (hence the waitOn) go and render the template providing the data function as a reactive data source for the templates.
So I then set up my 2 routes for indexing studios and showing one studio using iron router and 'Controllers' as follows:
StudiosIndexController = RouteController.extend({
template: 'studiosIndex',
increment: 20,
limit: function () {
return parseInt(this.params.studiosLimit) || this.increment;
},
findOptions: function () {
return {
sort: this.sort,
limit: this.limit()
};
},
waitOn: function () {
return Meteor.subscribe('studios', this.findOptions());
},
studios: function () {
return Studios.find({}, this.findOptions());
},
data: function () {
return {
studios: this.studios()
};
}
});
ShowStudioController = RouteController.extend({
template: 'showStudio',
waitOn: function () {
return Meteor.subscribe('studio', this.params._id);
},
studio: function () {
return Studios.findOne(this.params._id);
},
data: function () {
return {
studio: this.studio()
};
}
});
Router.map(function () {
this.route('studiosIndex', {
path: '/',
controller: StudiosIndexController
});
this.route('showStudio', {
path: '/studios/:_id',
controller: ShowStudioController
});
});
Now this is great and works fine when displaying my index page. I get a list of collections which is reactive and the as i introduce new studios to the collection i see them get created on the server and on the client respectively. However in my show view when the view is rendered the data always seems to be empty. On dropping into my show controller's data function with a debugger I tried querying the Studios and this always returns undefined even when i try to fetch everything possible. Oddly I know that my publications are logging the correct id for a studio (using console.log). It seems that i get all the correct data up until the routing on the client side. The parameter ids are all correct but a find() call on studios always returns nothing. Am I missing something obvious here.
Its also worth noting i deleted my helpers for 'studios' and 'studio' in views/studios/index.js and views/studios/show.js respectively as my understanding is that this is what im doing with studios and studio in the controller. These helpers are now defined at the controller level and passsed to the reactive 'data' function. IS this correct?
TL;DR
With the code above my index action works however my show action fails where the data function always returns undefined and in fact on the show page i cannot get access to any of my studio information (Studios.find({}).count() always returns 0). I'm unsure how the 2 routes differ. Can anyone spot the issue?
Edit: Its also worth noting having looked through some of the iron router issues on github i have tried using this.ready(). The first time the route is run i hit data and this is false but then even wrapping my data helpers to wait for this.ready() gets an undefined return value when this.ready() returns true.
Extra Notes
Im running meteor 0.8.0 with latest 0.7.0 release of iron router and collection2 with simple schema, just in case you are interested / require this info.
EDIT: Possible solution
So having fiddled around it seems like my helpers are the issue. By using the 'studio' section and then calling this.studio() inside my data function this seems to break. If I change the code below:
ShowStudioController = RouteController.extend({
template: 'showStudio',
waitOn: function () {
return Meteor.subscribe('studio', this.params._id);
},
studio: function () {
return Studios.findOne(this.params._id);
},
data: function () {
return {
studio: this.studio()
};
}
});
to this
ShowStudioController = RouteController.extend({
template: 'showStudio',
waitOn: function () {
return Meteor.subscribe('studio', this.params._id);
},
data: function () {
return Studios.findOne(this.params._id);
}
});
My view renders correctly again. Im unsure where i saw this pattern however I had assumed the function assigned to 'studio' was essentially the same as doing
Template.showStudio.helpers({
studio: function () {
return Studios.findOne(this.params._id)
}
});
but it seems not!
For some odd reason, iron-router randomly returns undefined.
this.route('pollyShow', {
path: '/polly/:_id',
template: 'polly_show',
notFoundTemplate: 'notFound',
before: function () {
var id = this.params._id;
var poll = Polls.findOne({_id: id});
console.log(poll);
var ip_array = poll.already_voted;
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
ip_voted = ip_array.indexOf(data.host);
if (ip_voted > -1) {
Router.go('pollyResults', {_id: id});
}
});
},
data: function() {
return Polls.findOne({_id: this.params._id});
}
});
Sometimes it is returning normally while other times it just returns undefined.
Is there any reason behind this?
The problem occurs because the Polly collection is sometimes populated and at other times unpopulated when the route executes.
This problem can be prevented by explicitly waiting on a subscription using waitOn option in the route configuration.
From the docs:
By default, a new Meteor app includes the autopublish and insecure packages, which together mimic the effect of each client having full read/write access to the server's database. These are useful prototyping tools, but typically not appropriate for production applications. When you're ready, just remove the packages.
To remove the packages, call meteor remove <package-name>.
Then you need to explicitly publish records which you want to see on the client on the server:
server/publications.js:
Meteor.publish('all_of_polly', function () { return Polls.find({}); });
And subscribe to it on the client:
this.route('pollyShow', {
path: '/polly/:_id',
template: 'polly_show',
notFoundTemplate: 'notFound',
waitOn: function () { return Meteor.subscribe('all_of_polly'); }
// ...
});