I am coding a basic meteor app and I ask myself how to notify other session of a change.
I have a basic template showing details of a document.
And someone can delete this document,
How can I notify other session that was watching this document, that it was deleted and redirect them?
If your document is in a collection and the delete corresponds to removing the document from the collection, you can use Meteor's observe on the collection to register a callback when the document you're watching is removed:
Documents.find({_id: myDocumentId}).observe({
removed: function () {
console.log('document removed');
}
});
and from there do any DOM/Session manipulation you want to notify clients of the change.
If you display your document by accessing directly your collection client-side (i.e. you don't use a method), your page fields values should reactively disappear since the document does not exist anymore.
What you can do is add a field deleted to your document and when it changes to true, you display your notification. I would advise to use something like a modal, so the user cant dodge it (when he close it, you redirect).
It also means that instead of deleting a document, the other user just change its deleted field to true. Once you set it to true you can also set a time differed function to effectively delete the doc for example 5mn later:
Meteor.setInterval(function () {
Document.remove(yourDocumentID);
}, 300000)
Related
I have a publication based on server-side user permissions. I want it to be reactive to changes in these permissions.
// SERVER CODE
Meteor.publish("my_publication", function(parent_id) {
//fetch our parent record and lookup this user's permissions
var parent = ParentCollection.findOne({_id: parent_id});
var myPermissionsList = parent.permissionsDict[this.userId];
//use these permissions to make our query
ChildCollection.find({parent_id: parent_id, permissions: {$in: myPermissionsList}})
}
// CLIENT CODE
Tracker.autorun(function () {
Meteor.subscribe('my_publication', Session.get("my_parent_id"));
});
This properly returns all the elements of the "child" collection specified parent, as long as the parent says the user has at least one of the permissions in the child element's list. It does this without the user actually knowing what their permissions are, which is a requirement.
This behaves like one would expect in Meteor:
The subscription does automatically update if any of the returned ChildCollection elements are changed.
The subscription does automatically update if the client changes the "my_parent_id" Session variable, triggering the Tracker.autorun resubscribe.
The subscription does not automatically update if the permissions used to make the query (parent.permissionsDict[this.userId]) are changed.
We're looking for the best (highest performing) way to get an automatic update in the last case.
This article was a helpful, more detailed resource on the topic:
https://www.discovermeteor.com/blog/reactive-joins-in-meteor/
My current understanding is that I need to utilize cursor.observeChanges() to react to changes in my permissions query. However, I am not sure how this fits into the rest of the Meteor publish/subscribe model--where would I call this, and how could the callback instruct Meteor to republish "my_publication"?
I believe https://atmospherejs.com/mrt/reactive-publish addresses this, but I feel like I should try to get a better grasp on core reactivity in meteor before turning to an external package. I also lack an understanding about the performance costs.
Any help would be greatly appreciated!
You can use the reactive-publish package (I am one of authors):
Meteor.publish("my_publication", function(parent_id) {
this.autorun(function (computation) {
//fetch our parent record and lookup this user's permissions
var parent = ParentCollection.findOne({_id: parent_id}, {fields: {permissionsDict: 1}});
var myPermissionsList = parent.permissionsDict[this.userId];
//use these permissions to make our query
return ChildCollection.find({parent_id: parent._id, permissions: {$in: myPermissionsList}});
});
}
It is important that you limit the fields you are interested in the parent document, otherwise autorun would rerun every time any field changes in the document, even if you do not care/use that field.
For example I subscribe on myCollection with the limit = 10. And when I do:
myCollection.insert({}), I don't want the first element from collection to be pulled out.
From the Meteor docs
Cursors are a reactive data source. On the client, the first time you
retrieve a cursor's documents with fetch, map, or forEach inside a
reactive computation (eg, a template or autorun), Meteor will register
a dependency on the underlying data. Any change to the collection that
changes the documents in a cursor will trigger a recomputation. To
disable this behavior, pass {reactive: false} as an option to find.
Is this what you need, disabling reactivity?
myCollection.find({}, {limit: 10, reactive: false});
In my app an user has the option to delete all his notifications. As an event handler I have this:
Template.clearNotifications.events({
'click .clear-notifications': function() {
Notifications.remove({userId: Meteor.user()._id});
}
});
When it is called, I get this error:
Uncaught Error: Not permitted. Untrusted code may only remove documents by ID. [403]
Is this a permission I forgot to add, or is it generally not allowed to do this? And if so, what are my options to remove the user's notifications?
On the client, meteor only allows the removal of documents by id. Fortunately there's an easy solution - just iterate over all of the current user's notifications and remove each one:
Template.clearNotifications.events({
'click .clear-notifications': function() {
Notifications
.find({userId: Meteor.userId()})
.forEach(function(notification) {
Notifications.remove(notification._id);
});
}
});
Keep in mind that this will only remove notifications which the client knows about (those which have been published). If there are additional notifications in the database which also need to be removed (maybe you only published the 10 most recent documents), you would need to use a method. For example:
Meteor.methods({
removeAllNotifications: function() {
Notifications.remove({userId: this.userId});
}
});
Which you can invoke from the client with:
Meteor.call('removeAllNotifications');
Removing in untrusted area (client) works only when you pass _id of document:
var notification = Notifications.findOne({userId:Meteor.userId()});
Notifications.remove({_id:notification._id})
Explanation:
The behavior of remove differs depending on whether it is called by trusted or untrusted code. Trusted code includes server code and method code. Untrusted code includes client-side code such as event handlers and a browser's JavaScript console.
Trusted code can use an arbitrary Mongo selector to find the documents to remove, and can remove more than one document at once by passing a selector that matches multiple documents. It bypasses any access control rules set up by allow and deny. The number of removed documents will be returned from remove if you don't pass a callback.
As a safety measure, if selector is omitted (or is undefined), no documents will be removed. Set selector to {} if you really want to remove all documents from your collection.
Untrusted code can only remove a single document at a time, specified by its _id. The document is removed only after checking any applicable allow and deny rules. The number of removed documents will be returned to the callback.
http://docs.meteor.com/#remove
I understand that when writing code that depends on the collection being loaded into the client minimongo, that you should explicitly subscribe to the collection and pass in the appropriate callback for when it is finished loading.
My problem is that I store a lot of important subdocuments that my page needs to access in the users collection. I am using Meteor Accounts, and am trying to figure out a similar way to wait until the entire logged in user document is available. When using this to test:
console.log(Meteor.user());
the logged in case, it seems like it first registers an object with just the _id, and then sends the other fields later (I know I have to explicitly add other fields to publish from the server beyond email, etc.).
Is there a way for me to wait for the logged in user document to load completely before executing my code?
Thanks!
Deps.autorun (previously Meteor.autorun) reruns when something reactive changes, which might fit your use case:
Client js
Deps.autorun(function () {
if(Meteor.user() {
//Collection available
}
});
If you're using a subscription you can also use its callback. Have a read about it on the docs as you might have to customize it a bit, and remove the autopublish package as well as get your other collections set up to subscriptions
Server js:
Meteor.publish("userdata", function () {
//You might want to alter this depending on what you want to send down
return Meteor.users.find({}, {}});
});
Client js
Meteor.subscribe("userdata", function() {
//Collection available
});
I want to write a background program that will monitor the _changes feed of a CouchDB, and possibly update the document. The problem is that the update causes another _change, and I get an endless loop! What's the best way to avoid this?
For example, here is the specific scenario: I have a CouchApp where users modify documents through their browser. I also have a python program that creates a PDF version of a document and then attaches it as an attached file to the document itself. My problem is that doing the PUT Attachment to upload the PDF also triggers a document change. I have to be able to tell whether a change is being caused by the PDF upload, or not. It seems like it should be easy, but I can't think of a simple way to do it. I would rather keep the PDF generator program be "stateless", keeping any required state in the db itself.
Now, this can easily be done if I require that users who change the document set some sort of flag on the document to indicate that it needs to be processed. The trick is how to do it without requiring that.
I have come to the conclusion that a "_changes" listener should never modify the document that it listens to. In my case I decided to attach my PDF file to a separate document, in a separate "database" within couchdb, but using the same "_id" to make it easy to correlate. That way I don't trigger a "_change" on the same documents that I am listening to. I could not get past the need to require every client that changes the document to somehow "flag" it as requiring processing ( by deleting the existing attachment, or otherwise setting some "dirty" flag ). After much pondering, I think that this will be a rule-of-thumb for me : that you must not modify a document upon receiving a "_change" notification for that document. Has anyone else reached the same conclusion?
Use a filter function and and filter out the second change – either by the document structure change or by setting an additional flag to the changed document:
function(doc, req)
{
if(!doc.hasStructuralChange) { //fix this
return true;
}
return false;
}
or
function(doc, req)
{
if(!doc.changed) { //set doc.changed during first update
return true;
}
return false;
}
Edit: You can check for an attachment via if (doc._attachments)