I am try to publish posts created by the usernames in the following array each users has. I am adding this to the microscope practice app The error is I20140826-20:31:53.452(-4)? at Meteor.publish.Comments.find.postId [as _handler] (app/server/publications.js:2:13). Thanks in advanced here is the code.
publications.js
The loop is supposed to publish posts made by each username in the following array.
Meteor.publish('posts', function(options) {
for (u=0;u<this.user.profile.following.length;u++) {
f=profile.following[u].text();
return Posts.find({username:f}, options);
}
});
The Routes it will affect
Controllers
PostsListController = RouteController.extend({
template: 'postsList',
increment: 5,
limit: function() {
return parseInt(this.params.postsLimit) || this.increment;
},
findOptions: function() {
return {sort: this.sort, limit: this.limit()};
},
waitOn: function() {
return Meteor.subscribe('posts', this.findOptions());
},
posts: function() {
return Posts.find({}, this.findOptions());
},
data: function() {
var hasMore = this.posts().count() === this.limit();
return {
posts: this.posts(),
nextPath: hasMore ? this.nextPath() : null
};
}
});
NewPostsListController = PostsListController.extend({
sort: {submitted: -1, _id: -1},
nextPath: function() {
return Router.routes.newPosts.path({postsLimit: this.limit() + this.increment})
}
});
BestPostsListController = PostsListController.extend({
sort: {votes: -1, submitted: -1, _id: -1},
nextPath: function() {
return Router.routes.bestPosts.path({postsLimit: this.limit() + this.increment})
}
});
Router map
this.route('newPosts', {
path: '/new/:postsLimit?',
controller: NewPostsListController
});
this.route('bestPosts', {
path: '/best/:postsLimit?',
controller: BestPostsListController
});
Your publish function is wrong, are you aware that your loop is useless because it is exiting upon encountering the first return ?
Even if you were aggregating the cursors accumulated in the loop this wouldn't work because at the moment a publish function can only return multiple cursors from DIFFERENT collections.
You need to use the appropriate mongo selector here, which is probably $in.
Also, profile.following is not even defined in the publish function, and iterating over an array is done by checking the iterator variable against the array length (profile.following.length), or better yet using Array.prototype.forEach or _.each.
I think this is what you're trying to do :
Meteor.publish("posts",function(options){
if(!this.userId){
this.ready();
return;
}
var currentUser=Meteor.users.findOne(this.userId);
return Posts.find({
username:{
$in:currentUser.profile.following
}
},options);
});
You should definitely read resources about JavaScript itself before digging any further in Meteor, if you're following the Discover Meteor book I think they provide some good JS tutorials for beginners.
Related
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
});
});
I have data stored in firebase in the following structure (figure 1). I followed the guidelines for structuring data and saved it in a flat structure with key-val pairs on events and users to allow for a many to many relationship reference. I want to user a userid to look up events that a user has access to, in pure javascript this is simple (see figure 2) however it is proving difficult with angularfire as I'd like to use a firebaseObject or array. Does anyone know how to perform a query like this?
Figure 1.
{
users: {
user_id1: {
events: {
event_id1: true,
event_id2: true
}
},
user_id2: {
events: {
event_id3: true,
event_id4: true
}
},
user_idN...
},
events: {
event_id1: {
users: {
user_id1: true
}
},
event_id2: {
users: {
user_id1: true
}
},
event_idN...
}
}
Figure 2
// List all of user_id1's events
var ref = new Firebase("https://<<example>>.firebaseio.com/");
// fetch a list of user_id1's events
ref.child("users/user_id1/events").on('child_added', function(snapshot) {
// for each event, fetch it and print it
String groupKey = snapshot.key();
ref.child("events/" + groupKey).once('value', function(snapshot) {
console.log(snapshot.val());
});
});
This is a great case for using $extend in AngularFire.
You're sharing the $event_id key so can load the events after, the user is retrieved.
app.factory("UserFactory", function($firebaseObject) {
return $firebaseObject.$extend({
getEvent: function(eventId) {
var eventRef = new Firebase('<my-firebase-app>/events').child(eventId);
return $firebaseObject(eventRef);
}
});
});
app.controller('MyCtrl', function($scope, UserFactory) {
var userRef = new Firebase('<my-firebase-app>').child("users/user_id1/");
var user = new UserFactory();
$scope.event = user.getEvent(user.events.event_id1);
});
See the API reference for more information.
I am trying to make sing post page a route where it does a several things using iron:router
Uses the template postPage
Subscribes to publication of singlePost, userStatus (shows status and info of Author of single post page'), comments .
Grabs Comments documents that has field of postId : this.params._id
Increments Comments List by Session.get('commentLimit')
Here is the code I currently have.
Router.js
Router.route('/posts/:_id', {
name: 'postPage',
subscriptions: function() {
return [
Meteor.subscribe('singlePost', this.params._id),
Meteor.subscribe('userStatus'),
Meteor.subscribe('comments', {
limit: Number(Session.get('commentLimit'))
})
];
},
data: function() {
return Posts.findOne({_id:this.params._id});
},
});
Publications.js
Meteor.publish('singlePost', function(id) {
check(id, String);
return Posts.find(id);
});
Meteor.publish('comments', function(options) {
check(options, {
limit: Number
});
return Comments.find({}, options);
});
Template.postPage.onCreated
Template.onCreated( function () {
Session.set('commentLimit', 4);
});
Template.postPage.helpers
Template.postPage.helpers({
comments: function () {
var commentCursor = Number(Session.get('commentLimit'));
return Comments.find({postId: this._id}, {limit: commentCursor});
},
});
Template.postPage.events
Template.postPage.events({
'click a.load-more-comments': function (event) {
event.preventDefault();
Session.set('commentLimit', Number(Session.get('commentLimit')) + 4)
}
});
Everything works fine, but I found one thing to be inconsistent.
Here is the problem I am having...
User goes into single post page and adds comment (everything works fine).
User goes into a different single post page and adds comment (everything works fine).
Here is where the problem begins
The user at any time, goes into another route that is not the single post page.
User goes back into single post page
The comments are not showing.
New comments will be added into DB but still wont show
This problem only goes away when meteor reset or manual deletion of all comments in MongoDB is performed.
Is there a better way that I can code my routing and related code to stop this weird behavior from happening?
Or even if there is a better practice.
Your publish is publishing comments without any postId filter.
Your helper, filters by postId. Maybe the 4 comments that get published are the ones that do not belong to the current post that is open?
Could you try updating, your subscription to
Meteor.subscribe('comments', {
postId: this.params._id
}, {
limit: Number(Session.get('commentLimit'))
})
and your publication to
Meteor.publish('comments', function(filter, options) {
check(filter, {
postId: String
});
check(options, {
limit: Number
});
return Comments.find(filter, options);
});
so that only the same posts' comments are published?
I have figured it out. I have updated the following codes.
So far it is not showing weird behavior...
Publications.js
Meteor.publish('comments', function(postId, limit) {
check(postId, String);
check(limit, Number);
return Comments.find({postId:postId}, {limit:limit});
});
Router.js
Router.route('/posts/:_id', {
name: 'postPage',
subscriptions: function () {
return [
Meteor.subscribe('singlePost', this.params._id),
Meteor.subscribe('userStatus'),
Meteor.subscribe('comments', this.params._id, Number(Session.get('commentLimit')))
];
},
data: function() {
return Posts.findOne({_id:this.params._id});
},
});
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.
Edit 11/16/14: Version Information
DEBUG: Ember : 1.7.0 ember-1.7.0.js:14463
DEBUG: Ember Data : 1.0.0-beta.10+canary.30d6bf849b ember-1.7.0.js:14463
DEBUG: Handlebars : 1.1.2 ember-1.7.0.js:14463
DEBUG: jQuery : 1.10.2
I'm beating my head against a wall trying to do something that I think should be fairly straightforward with ember and ember-data, but I haven't had any luck so far.
Essentially, I want to use server data to populate a <select> dropdown menu. When the form is submitted, a model should be created based on the data the user chooses to select. The model is then saved with ember data and forwarded to the server with the following format:
{
"File": {
"fileName":"the_name.txt",
"filePath":"/the/path",
"typeId": 13,
"versionId": 2
}
}
The problem is, the typeId and versionId are left out when the model relationship is defined as async like so:
App.File = DS.Model.extend({
type: DS.belongsTo('type', {async: true}),
version: DS.belongsTo('version', {async: true}),
fileName: DS.attr('string'),
filePath: DS.attr('string')
});
The part that is confusing me, and probably where my mistakes lie, is the controller:
App.FilesNewController = Ember.ObjectController.extend({
needs: ['files'],
uploadError: false,
// These properties will be given by the binding in the view to the
//<select> inputs.
selectedType: null,
selectedVersion: null,
files: Ember.computed.alias('controllers.files'),
actions: {
createFile: function() {
this.createFileHelper();
}
},
createFileHelper: function() {
var selectedType = this.get('selectedType');
var selectedVersion = this.get('selectedVersion');
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path'
});
var gotDependencies = function(values) {
//////////////////////////////////////
// This only works when async: false
file.set('type', values[0])
.set('version', values[1]);
//////////////////////////////////////
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
this.set('uploadError', true);
}.bind(this);
file.save().then(onSuccess, onFail);
}.bind(this);
Ember.RSVP.all([
selectedType,
selectedVersion
]).then(gotDependencies);
}
});
When async is set to false, ember handles createRecord().save() POST requests correctly.
When async is true, ember handles GET requests perfectly with multiple requests, but does NOT add the belongsTo relationships to the file JSON during createRecord().save(). Only the basic properties are serialized:
{"File":{"fileName":"the_name.txt","filePath":"/the/path"}}
I realize this question has been asked before but I have not found a satisfactory answer thus far and I have not found anything that suits my needs. So, how do I get the belongsTo relationship to serialize properly?
Just to be sure that everything is here, I will add the custom serialization I have so far:
App.ApplicationSerializer = DS.RESTSerializer.extend({
serializeIntoHash: function(data, type, record, options) {
var root = Ember.String.capitalize(type.typeKey);
data[root] = this.serialize(record, options);
},
keyForRelationship: function(key, type){
if (type === 'belongsTo') {
key += "Id";
}
if (type === 'hasMany') {
key += "Ids";
}
return key;
}
});
App.FileSerializer = App.ApplicationSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
type: { serialize: 'id' },
version: { serialize: 'id' }
}
});
And a select:
{{ view Ember.Select
contentBinding="controller.files.versions"
optionValuePath="content"
optionLabelPath="content.versionStr"
valueBinding="controller.selectedVersion"
id="selectVersion"
classNames="form-control"
prompt="-- Select Version --"}}
If necessary I will append the other routes and controllers (FilesRoute, FilesController, VersionsRoute, TypesRoute)
EDIT 11/16/14
I have a working solution (hack?) that I found based on information in two relevant threads:
1) How should async belongsTo relationships be serialized?
2) Does async belongsTo support related model assignment?
Essentially, all I had to do was move the Ember.RSVP.all() to after a get() on the properties:
createFileHelper: function() {
var selectedType = this.get('selectedType');
var selectedVersion = this.get('selectedVersion');
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path',
type: null,
version: null
});
file.set('type', values[0])
.set('version', values[1]);
Ember.RSVP.all([
file.get('type'),
file.get('version')
]).then(function(values) {
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
alert("failure");
this.set('uploadError', true);
}.bind(this);
file.save().then(onSuccess, onFail);
}.bind(this));
}
So I needed to get() the properties that were belongsTo relationships before I save the model. I don't know is whether this is a bug or not. Maybe someone with more knowledge about emberjs can help shed some light on that.
See the question for more details, but the generic answer that I worked for me when saving a model with a belongsTo relationship (and you specifically need that relationship to be serialized) is to call .get() on the properties and then save() them in then().
It boils down to this:
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path',
type: null,
version: null
});
// belongsTo set() here
file.set('type', selectedType)
.set('version', selectedVersion);
Ember.RSVP.all([
file.get('type'),
file.get('version')
]).then(function(values) {
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
alert("failure");
this.set('uploadError', true);
}.bind(this);
// Save inside then() after I call get() on promises
file.save().then(onSuccess, onFail);
}.bind(this));