I found this error in the server console:
Exception in defer callback: TypeError: undefined is not a function
at packages/ddp/livedata_server.js:1054:1
at Array.forEach (native)
at Function..each..forEach (packages/underscore/underscore.js:105:1)
at [object Object]._.extend._callStopCallbacks (packages/ddp/livedata_server.js:1053:1)
at [object Object]._.extend._deactivate (packages/ddp/livedata_server.js:1043:1)
at packages/ddp/livedata_server.js:803:1
at Function..each..forEach (packages/underscore/underscore.js:113:1)
at [object Object]._.extend._deactivateAllSubscriptions (packages/ddp/livedata_server.js:802:1)
at packages/ddp/livedata_server.js:444:1
at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
And here the code I use:
/* Source: http://phucnguyen.info/blog/how-to-publish-to-a-client-only-collection-in-meteor/ */
var subs = {};
Meteor.publish(
// THROW AN 'EXCEPTION IN DERFER CALLBACK: ...'
'helperPublication',
function () {
var subscription = this;
subs[subscription._session.id] = subscription;
Datasets.find().map(function (dataset) {
subscription.added(
'subdatasets',
dataset._id,
{
name: dataset.name,
data: [], // To avoid "Uncaught TypeError: Cannot read property 'length' of undefined" error on load
peaks: [] // Idem
}
)
});
subscription.onStop();
subscription.ready()
});
You can find the whole app in the following meteorpad: http://meteorpad.com/pad/6NDneym2qEW7pF9JM/ClearChrom
Ok, So with that info; I think the best way to go about doing this is to have separate collection for that data. Because then you can easily change how much data should be displayed. The publication for that collection could look like this:
Meteor.publish('data', function publishData (limit) {
return DataCollection.find({}, {
fields: {
name: 1,
data: 1,
peaks: 1
},
limit: limit
})
});
Note that the callback of the publication takes an argument limit. You can now subscribe to it like this:
Meteor.subscribe('data', 3)
And whenever you need more data you can just:
Meteor.subscribe('data', 6)
So this solution is reactive and very clean (in my opinion at least).
I also checked your existing script:
var subs = {};
Meteor.publish(
'helperPublication',
function (limit) {
var subscription = this;
subs[subscription._session.id] = subscription;
// I'd use forEach here, because you're not modifying the document
Datasets.find().forEach(function (doc) {
subscription.added(
'subdatasets',
doc._id,
{
name: doc.name,
// Whith the slice function you can get a subset of the data array
data: doc.data.slice(0, limit),
peaks: doc.peaks
}
)
});
// Calling `subscription.onStop` without an argument doesn't do anything
// Except maybe through an error
subscription.onStop(function () {
// clean the subscription from subs again
delete subs[subscription._session.id];
});
// This shouldn't be necessary.
// subscription.ready()
});
There are some issues with this still. For one thing I'd advise you to try avoiding meteor attributes with an underscore in front. They might be removed or changed in future releases.
Related
I have a peice of code;
Router.configure({
layoutTemplate: 'master_layout'
});
Router.map(function(){
this.route('generatePDF', {
path: '/pdf',
where: 'server',
action: function() {
console.log(voters.findOne().fetch());
var voters = voters.find();
....
}
});
How can I use any given collection inside action of routes.
i get error as below;
W20160510-20:19:29.909(5.5)? (STDERR) TypeError: Cannot call method 'findOne' of undefined
W20160510-20:19:29.910(5.5)? (STDERR) at [object Object].action (lib/routes.js:40:29)
W20160510-20:19:29.910(5.5)? (STDERR) at boundNext (packages/iron_middleware-stack/lib/middleware_stack.js:251:1)
Try this:
action: function () {
this.render('someTemplate',{
data: function(){
return Voters.find()
}
});
}
You've got multiple errors in your code.
Firstly, voters is undefined in the console.log line (as your error log says).
Secondly, Voters.findOne() returns a single document (assuming you have a collection called Voters). You can't call fetch on a document.
You can call fetch on a cursor. For example, Voters.find().fetch() would be fine.
Hope that helps
I have a simple Meteor subscription, and I display a loading message while the data is being loaded. But I don't know how to display error message if subscription failed.
export const MyAwesomeComponent = createContainer(() => {
let sub = Meteor.subscribe('some-data');
if (!sub.ready()) return { message: 'Loading...'};
if (sub.failed()) return { message: 'Failed.' }; // How to do this?
return {
data: Data.find().fetch()
}
}, MyInternalRenderComponent);
Problem is, the subscription object doesn't have a failed() method, only a ready() query. How to pass the failure of a subscription as props in a createContainer() method?
I know the Meteor.subscribe method has an onStop callback for this case, but I don't know how to glue it toghether that to pass a property.
After a lot of researching I managed to get this working and I think it answers your question.
Bear in mind I'm using Meteor 1.6, but it should give you the info to get it working on your side.
On the publication/publish:
try {
// get the data and add it to the publication
...
self.ready();
} catch (exception) {
logger.error(exception);
// send the exception to the client through the publication
this.error(new Meteor.Error('500', 'Error getting data from API', exception));
}
On the UI Component:
const errorFromApi = new ReactiveVar();
export default withTracker(({ match }) => {
const companyId = match.params._id;
let subscription;
if (!errorFromApi.get()) {
subscription = Meteor.subscribe('company.view', companyId, {
onStop: function (e) {
errorFromApi.set(e);
}
});
} else {
subscription = {
ready: () => {
return false;
}
};
}
return {
loading: !subscription.ready(),
company: Companies.findOne(companyId),
error: errorFromApi.get()
};
})(CompanyView);
From here all you need to do is get the error prop and render the component as desired.
This is the structure of the error prop (received on the onStop callback from subscribe):
{
error: String,
reason: String,
details: String
}
[Edit]
The reason there is a conditional around Meteor.subscribe() is to avoid an annoying infinite loop you'd get from the natural withTracker() updates, which would cause new subscriptions / new errors from the publication and so on.
in my custom Polymer element, i have a method that queries a firebase instance like so:
_updateUser: function(user) {
if(this._firebaseRef) {
this._firebaseRef.off()
}
if(user) {
var userLocation = ['xxxxx.firebaseio.com', 'users', this.user.uid].join('/');
this._firebaseRef = new Firebase(userLocation);
var query = this._firebaseRef.orderByChild("username")
query.on("value", function(messageSnapshot) {
messageSnapshot.forEach(function(messageData){
this.set('usernames', []);
console.log(this);
});
},null, this);
}
},
However, I keep getting the following error Uncaught TypeError: this.set is not a function. It seems that this is not set as the context as expected as per the api documentation
May I know how I can call this.set in my callback successfully? Thanks.
You need to pass the context into the forEach function as well.
messageSnapshot.forEach(function (messageData) {...}, this)
or
messageSnapshot.forEach(function (messageData) {...}.bind(this))
I am doing a find on a collection of nodes and am then storing the info in a variable.
I am then console.logging this data to see the contents of it. The thing is, my data stays an empty array for a split second and then a new console.log is done with an array of data. See my code below:
Template.temperature.helpers({
node() {
let node = Nodes.find({_id:Session.get('selectedNode')});
console.log(node);
return '';
}
});
My console output:
1: []
2: [Object]
What could be the reason for this?
In your router, subscribe within waitOn:
Router.route('/home', {
waitOn: function() {
return [
Meteor.subscribe('nodes'),
];
},
action: function() {
if (this.ready()) {
this.render();
}
},
});
This will ensure that the route will wait until the subscription is completed before executing the route. It uses the meteor loading hook, so the wait will utilize whatever loading screen or animation you ahve setup.
My angular controller looks like this:
angular.module("campos").controller("HomeCtrl", ['$scope', '$meteor', '$rootScope', '$state', '$modal',
function ($scope, $meteor, $rootScope, $state, $modal) {
// $scope.users = $meteor.collection(Meteor.users, false).subscribe('users');
$meteor.autorun($scope, function () {
$scope.$meteorSubscribe('myOrgnization', 'crown').then(function () {
$scope.organization.forEach(function (org) {
console.log(org._id);
});
},
function (error) {
console.log(error);
});
});
$scope.organization = $meteor.collection(function(){
return Organizations.find({});
});
}
]);
The server code looks like :
Meteor.publish('myOrgnization', function(org){
return Organizations.find({
'code' : org
});
});
The client controller finished properly, from the console I can get the the record id properly. However, I got an exception thrown out:
TypeError: undefined is not a function
at http://localhost:8026/packages/urigo_angular.js?de756130fe1ce5aaa41d3967997c1b2090bcdd1b:390:12
at Array.forEach (native)
at Function._.each._.forEach (http://localhost:8026/packages/underscore.js?46eaedbdeb6e71c82af1b16f51c7da4127d6f285:149:11)
at diffArray (http://localhost:8026/packages/urigo_angular.js?de756130fe1ce5aaa41d3967997c1b2090bcdd1b:388:5)
at updateCollection (http://localhost:8026/packages/urigo_angular.js?de756130fe1ce5aaa41d3967997c1b2090bcdd1b:1310:3)
at http://localhost:8026/packages/urigo_angular.js?de756130fe1ce5aaa41d3967997c1b2090bcdd1b:1154:11
at http://localhost:8026/packages/angular_angular.js?feeaf4750c9fd3ceafa9f712f99a21ee6fef9b41:17819:31
at completeOutstandingRequest (http://localhost:8026/packages/angular_angular.js?feeaf4750c9fd3ceafa9f712f99a21ee6fef9b41:5529:10)
at http://localhost:8026/packages/angular_angular.js?feeaf4750c9fd3ceafa9f712f99a21ee6fef9b41:5806:7
home.ng.js:8 dPmZoCeJ9YbKxKA4u
It seems coming from angular defer, but I think I am following the subscribe syntax properly ? even I change it to use $meter.subscribe, it throws the same exception.
Any help will be much appreciated.
Thanks in advance.
You should assign the $scope collection after the promise returns:
$scope.$meteorSubscribe('myOrgnization', 'crown').then(function () {
$scope.organization = $scope.$meteorCollection(function(){
return Organizations.find({});
});
});
I would also suggest passing an object for your publish arguments:
$scope.$meteorSubscribe('myOrginization', {arg1: 'crown'}).then(...)
Then of course on the server you would get:
Meteor.publish('myOrgnization', function(org){
return Organizations.find({
'code' : org.arg1
});
});