On my server side, I publish a Groups collection. This relies on publishing groups that match another Servers collection.
Meteor.publish('groups', function() {
const servers = Servers.find({}); // simplified code
return Groups.find({serverId: {$in: servers}});
});
A client view then subscribes to that:
self.autorun(() => {
self.subscribe('groups');
});
This generally works fine. The problem comes when I add insert a server in the Servers collections, there is no indication to the publication to update the Groups, therefore this added server's Groups don't publish to the client.
What is the right way to handle this?
The Meteor Guide is always a good starting point for these kind of problems. Go here https://guide.meteor.com/data-loading.html#publishing-relations. TLDR; just add reywood:publish-composite package. :)
Let me know.
A possible straightforward approach here is to create a separate publication for Servers and then make groups Servers-dependent.
// Server
Meteor.publish("servers", function() {
return Servers.find({}, { fields: { _id: 1 } });
});
Meteor.publish("groups", function(serversIds) {
return Groups.find({ serverId: { $in: serversIds } });
});
// Client
self.autorun(() => {
self.subscribe("servers");
});
self.autorun(() => {
const serversIds = Servers.find().map(server => server._id);
self.subscribe("groups", serversIds);
});
Related
In Meteor, one can add additional fields to the root-level of the new user document like so:
// See: https://guide.meteor.com/accounts.html#adding-fields-on-registration
Accounts.onCreateUser((options, user) =>
// Add custom field to user document...
user.customField = "custom data";
return user;
});
On the client, one can retrieve some data about the current user like so:
// { _id: "...", emails: [...] }
Meteor.user()
By default, the customField does not exist on the returned user. How can one retrieve that additional field via the Meteor.user() call such that we get { _id: "...", emails: [...], customField: "..." }? At present, the documentation on publishing custom data appears to suggest publishing an additional collection. This is undesired for reasons of overhead in code and traffic. Can one override the default fields for Meteor.user() calls to provide additional fields?
You have a couple of solutions that you can use to solve this.
Null Publication
Meteor.publish(null, function () {
if (this.userId !== null) {
return Meteor.users.find({ _id: this.userId }, { fields: { customField: 1 } });
} else {
return this.ready();
}
}, { is_auto: true });
This will give you the desired result but will also result in an additional database lookup.. While this is don't by _id and is extremely efficient, I still find this to be an unnecessary overhead.
2.Updating the fields the Meteor publishes for the user by default.
Accounts._defaultPublishFields.projection = { customField: 1, ...Accounts._defaultPublishFields.projection };
This has to be ran outside of any Meteor.startup blocks. If ran within one, this will not work. This method will not result in extra calls to your database and is my preferred method of accomplishing this.
You are actually misunderstanding the documentation. It is not suggesting to populate and publish a separate collection, just a separate publication. That's different. You can have multiple publications/subscriptions that all feed the same collection. So all you need to do is:
Server:
Meteor.publish('my-custom-user-data', function() {
return Meteor.users.find(this.userId, {fields: {customField: 1}});
});
Client:
Meteor.subscribe('my-custom-user-data');
I've gone through a bunch of tutorials and docs but cannot seem to be able to update on page when data changes in Firestore (NOTE: not Firebase)
Heres what I have currently which is working fine except if data changes in the DB it is not reflected on the page itself unless I refresh. Code below is within script tags:
import { recipeRef } from '../../firebase';
export default {
data() {
return {
recipes: []
}
},
firestore: {
recipes: recipeRef
},
created() {
db.collection('recipes').get().then((onSnapshot) => {
this.loading = false
onSnapshot.forEach((doc) => {
let data = {
'id': doc.id,
'name': doc.data().name
}
this.recipes.push(data)
})
})
}
I'm not using Vuex. Adding data, editing and reading works fine. Just not reflecting changes once data has changed. Maybe there is a life cycle hook Im supposed to be using? For "onSnapshot" - Ive tried "snap", "querySnapshot" etc. No luck.
Thanks in advance.
Remove the get() and just replace with snapshot - like so
created() {
db.collection('recipes').onSnapshot(snap => {
let foo = [];
snap.forEach(doc => {
foo.push({id: doc.id, name: doc.data().name})
});
}
});
I am not familiar with the firestore API, but glancing through the docs, it looks like calling get() is how you query a single time. Where you have onSnapshot should really be querySnapshot -- that is, the results of a one query. See:
https://firebase.google.com/docs/firestore/query-data/get-data
versus:
https://firebase.google.com/docs/firestore/query-data/listen
So to get live updates, it looks like you need to create a listener, like so:
db.collection('recipes')
.onSnapshot(function(snapshot) {
snapshot.forEach(function(doc) {
// Find existing recipe in this.recipes
// and swap in the new data
});
}, function(error) {
// handle errors
});
I think you will need to add that listener in addition to the get() query you are currently doing. Hope this helps!
With autopublish package is removed, While this Meteor code is running, a different userId has been confirmed on 2 different browsers consoles Meteor.userId();
But when a string is typed in the inputText of one of them, and a collection.insert is done, the other shows the same string.
I thought that this.userId was good enough for the server to publish only the documents that belongs to each of the different clients simultaneously.
Why is this happening and how to fix it? Thanks
Server
Meteor.publish('displayCol', function () {
return DisplayCol.find({userId: this.userId});
});
DisplayCol.before.insert(function (userId, doc) {
doc.userId = userId;
});
Client
Template.index.helpers({
taskInputs: function () {
var ready = Meteor.subscribe('displayCol').ready();
var data = DisplayCol.find({});
return {items: data, ready: ready};
}
});
Do you have autopublish still installed? If so, both clients will get everything automatically. Remove it with 'meteor remove autopublish'
You can also add the {userId: Meteor.userId()} condition on the client side.
I'm using subscriptions manager with iron-router and my problem is this one.
I have a collection "participants" with 2 publications: allParticipants and todayParticipants.
if I go to this page:
Router.map(function () {
this.route('winners', {
waitOn: function () {
return [subs.subscribe('allWinners'),
subs.subscribe('allParticipants')];
console.log("subscribed!");
},
data: function () {
return {
winners: Winners.find(),
participants: Participants.find(),
loginBox: "True"
}
}
});
AllParticipants publication is subscribed and put in cache by the subscription manager package.
If after this, I go to this page:
Router.map(function () {
this.route('participants', {
path: '/',
waitOn: function () {
return subs.subscribe('todayParticipants');
},
data: function () {
return {
participants: Participants.find()
}
}
});
I'm expecting to subscribe only the todayParticipants but as my subscription is automatically named "Participants", It uses the cached subscription from the previous page being allParticipants.
Is there a way to change the name of my subscriptions in order to have each of them in the right cache?
Thanks.
What I do in my waitOn function is first stop my subscriptions like
if (App.subs) {
for (name in App.subs) {
App.subs[name].stop();
}
}
And then I create new subscriptions
App.subs = {
settings: Meteor.subscribe('settings', project),
...
};
return [App.sub.settings, .....];
Hope this helps!
Today, there seems to be no solution to this problem.
More explanation here: https://github.com/meteorhacks/subs-manager/issues/11
What I'm doing now is using a very limited number of subscriptions (filtered mainly on user) and then I create as much data objects as I want filtering my subscriptions in different ways.
I've created a method that checks if an email already has an account:
insertGroupMember: function(eventId, memberDetails) {
var memberAccount = Meteor.users.findOne({'emails.address': memberDetails.email});
if (memberAccount) {
console.log('Existing User')
} else {
console.log('Create User')
}
}
But will only receive a result when I am subscribed to a publication with all users.emails. How can I achieve the same results without having to publish everyone's email? I think thats kind of bad for security/privacy, right?
You are correct - you don't want to publish all of the users to the client just to accomplish this. The best solution is to create a method defined only on the server, and then call it from the client.
server/methods.js
Meteor.methods({
insertGroupMember: function(eventId, memberDetails) {
...
}
});
client/someTemplate.js
Meteor.call('insertGroupMember', eventId, memberDetails, function (err, result){
...
});