I've made a working chat with meteor and mongodb, but I want to play a sound or something when there is a new message. However, I don't know how to check if data is updated. I could check if there is a new message by counting the messages before and after the update, but I just don't know how to check for an update.
So my question here is: How do I check for an update in the data?
I have a website that needs to pop up a toastr alert whenever a new message arrives. My collection is called "Alerts". This is what I do:
Alerts.find({notified: false}).observeChanges({
added: function(id, doc) {
Alerts.update(id, {
$set: {
notified: true
}
});
toastr.info(foo, bar);
}
});
Whenever a new alert is created whose field "notified" is false, a toastr alert will be created and that alert will be marked as "notified: true".
Alternatively you could do the same thing but create a separate collection of "notifications" that when observed, are removed from the collection as well that are a distinct collection from your chat messages collection.
You could create a tailing cursor on the oplog collection, so you get a new document whenever something (anything!) in the database changes. But that's not really an elegant solution, because that handler would need to process a lot of junk.
It might be better to have the routine which writes the message to the database also inform any currently online users. There is really no good reason to go the detour over the database.
Related
I have an Angular 4 application in which I need to add the following functionality:
There is a component with a list of objects. When the user double clicks on one of them, the app retrieves from a DB a list of objects and it should scroll to where the object appears.
I'd like to know how I could move to the desired position in the data once that it has been displayed in the browser. Right now, I have the following code:
let objElement = document.querySelector("#object_"+searchItem._objectID);
if (objElement){
objElement.scrollIntoView();
console.log("****** SCROLLING TO OBJECT");
}
The problem is that, the first time that I load the data from the DB, it seems that 'document.querySelector' returns null, as if the HTML wasn't 100% constructed yet, so it doesn't scroll to the position. If I try to locate the element again, it scrolls perfectly (as it doesn't reload the data from the DB).
Is there a "more Angular" way of doing this? I'm trying to find an example like this in the Angular Router documentation but I can't find anything...
EDIT:
To make things clearer, this is the pseudo-code that I run when the user selects an object:
if(selectedObject IS IN currentLoadedObjects) {
scrollTo(selectedObject); // This function runs the code above
}
else { // The object is in a different list, so retrieve it from the DB
ObjectService.getObjectListFromDB(selectedObject)
.subscribe((returnedList) => {
displayObjectList(returnedList); // Basically, this function parses the returned data, which is displayed in the template using an *ngFor loop
scrollTo(selectedObject);
});
}
As you can see, I try to scroll to the object inside the 'subscribe' method, once that I have the data from the database and after I've parsed it. The object list is pretty big, so it takes 1-2 seconds to be displayed in the browser.
Thanks!
I´m starting with IndexedDB and to not reinvent the wheel I´m using Dexie.js https://github.com/dfahlander/Dexie.js
I created the database, I added data and now I´m creating a generic function that get a CSV and populate the database in anothers tables.
So, more or less my code is
// Creation and populate database and first table
var db = new Dexie("database");
db.version(1).stores({table1: '++id, name'});
db.table1.add({name: 'hello'});
Until here all is OK
Now, in success of ajax request
db.close();
db.version(2).stores({table2: '++id, name'});
db.open();
db.table2.add({name: 'hello'});
First time this code run everything is OK, but next time I get this error
VersionError The operation failed because the stored database is a
higher version than the version requested.
If I delete database and run code again only first time works OK.
Any idea? I don´t like too much IndexedDB version way, it´s looks frustrating and I don't get lot of help in the Net
Thanks.
Edit:
I discover the ¿problem/bug/procedure?. If I don´t add nothing before any version modification I haven't this issue, but does somebody know if is this the normal procedure?
So.. if this is the procedure I can't add any table dinamycally with a generic method. First all declarations and then add values. Any possibility to add a table after add values?
Edit again... I just realized that I could create another database. I'll post results. But any information about this issue is welcome :)
Edit again... I created dinamycally another database and everybody is happy!!
That is because the second time the code runs, your database is on version 2, but your main code still tries to open it at version 1.
If not knowing the current version installed, try opening dexie in dynamic mode. This is done by not specifying any version:
var db = new Dexie('database');
db.open().then(function (db) {
console.log("Database is at version: " + db.verno);
db.tables.forEach(function (table) {
console.log("Found a table with name: " + table.name);
});
});
And to dynamically add a new table:
function addTable (tableName, tableSchema) {
var currentVersion = db.verno;
db.close();
var newSchema = {};
newSchema[tableName] = tableSchema;
// Now use statically opening to add table:
var upgraderDB = new Dexie('database');
upgraderDB.version(currentVersion + 1).stores(newSchema);
return upgraderDB.open().then(function() {
upgraderDB.close();
return db.open(); // Open the dynamic Dexie again.
});
}
The latter function returns a promise to wait until it's done before using the new table.
If your app resides in several browsers, the other windows will get their db connection closed as well so they can never trust the db instance to be open at any time. You might want to listen for db.on('versionchange') (https://github.com/dfahlander/Dexie.js/wiki/Dexie.on.versionchange) to override the default behavior for that:
db.on("versionchange", function() {
db.close(); // Allow other page to upgrade schema.
db.open() // Reopen the db again.
.then(()=> {
// New table can be accessed from now on.
}).catch(err => {
// Failed to open. Log or show!
});
return false; // Tell Dexie's default implementation not to run.
};
I have an application where users can follow other users. I want to have a real-time update system, display the total count of followers a user has.
I've just started playing around with Firebase and Pusher, but I don't understand one thing - will each user have their own 'channel'? How would I handle such thing as a follower counter update?
I followed the tutorial and saw that push method can create lists, so one solution I can see is having a list of all the users, each user being an object something like this:
{ username: 'john_doe', follower_count: 6 }
But I don't understand how would I actually track it on the front end? Should I have a filter, something like this?
var user = users.filter(function(user) { return user.username === 'john_doe'; });
// code to display user.follower_count
Or is there a different way that I'm missing?
Most of Firebase logic is based on listeners, you can add a listener to events happening in your collections and when those events happen, you do something.
One way to go about this would be:
var myFollowerListRef = new Firebase(PATH_TO_YOUR_COLLECTION);
myFollowerListRef.on("value", function(snapshot) {
console.log(snapshot.length);
});
This way, every time your follower collection changes, the asynchronous function fires and you can do what you want with the fresh data.
For more information:
https://www.firebase.com/docs/web/guide/retrieving-data.html
Hope this helps, I'm still a beginner in Firebase.
#Th0rndike's approach is the simplest and works fine for relatively short lists. For longer lists, consider using a transaction. From the Firebase documentation on saving transactional data:
var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
return (current_value || 0) + 1;
});
But I recommend that you read the entire Firebase guide. It contains solutions for a lot of common use-cases.
Let's say I want to show the same notification each time something happens. That's what I currently use:
chrome.notifications.create(id, {
type:"basic",
title:"Title",
message:"My message",
iconUrl: "icon.png",
}, notificationResult);
But sometimes the notification doesn't appear.
Is that an id thing ? Do I need to reuse an already created notification ? Can I not create a new notification with the same id ?
I tried to do a var notification = chrome.notifications.create(id .... ) and do a notification.show() in case I already created one with the same id but that also didn't solve it.
So - do I need to recreate an existing notification each time I want to show the same one (which currently doesn't work for me), or is there a different way? How to make sure it pops every time?
The id in the create function is specifically for reusing. IDs must be unique. If you use create with an ID of an existing notification, it basically behaves like an update.
If a notification exists, it may no longer be shown but only be visible in the Message Center. In this case, the notification IS updated - but not shown again.
The API docs specify that you can pass an empty string to the notification to get a unique new id. If you need it, it is passed to the callback.
But if you do want to reuse the ID (ensuring that the notification is unique), you can use priority trick to make it show again.
You can clear the notification if its not use and if you want to use the same id.
For example :
function Notify(){
var my_notif_id="some_id";
//This will clear your previous notifcation with the same ID
chrome.notifications.clear(my_notif_id,function(){});
chrome.notifications.create(my_notif_id,options,function(){});
}
Now each time you call the notify function to display notification it will clear the old notification before displaying new notification and gets displayed.
UPDATED
As #Xan suggested, Its good to incorporate the create() method inside callback function of clear()
So here is the complete example :
function Notify(id, options){
//This will clear your previous notifcation with the same ID
chrome.notifications.clear(id, function() {
//inside callback function
chrome.notifications.create(id, options, function(){});
});
}
I've been trying to do Meteor's leaderboard example, and I'm stuck at the second exercise, resetting the scores. So far, the furthest I've got is this:
// On server startup, create some players if the database is empty.
if (Meteor.isServer) {
Meteor.startup(function () {
if (Players.find().count() === 0) {
var names = ["Ada Lovelace",
"Grace Hopper",
"Marie Curie",
"Carl Friedrich Gauss",
"Nikola Tesla",
"Claude Shannon"];
for (var i = 0; i < names.length; i++)
Players.insert({name: names[i]}, {score: Math.floor(Random.fraction()*10)*5});
}
});
Meteor.methods({
whymanwhy: function(){
Players.update({},{score: Math.floor(Random.fraction()*10)*5});
},
}
)};
And then to use the whymanwhy method I have a section like this in if(Meteor.isClient)
Template.leaderboard.events({
'click input#resetscore': function(){Meteor.call("whymanwhy"); }
});
The problem with this is that {} is supposed to select all the documents in MongoDB collection, but instead it creates a new blank scientist with a random score. Why? {} is supposed to select everything. I tried "_id" : { $exists : true }, but it's a kludge, I think. Plus it behaved the same as {}.
Is there a more elegant way to do this? The meteor webpage says:
Make a button that resets everyone's score to a random number. (There
is already code to do this in the server startup code. Can you factor
some of this code out and have it run on both the client and the
server?)
Well, to run this on the client first, instead of using a method to the server and having the results pushed back to the client, I would need to explicitly specify the _ids of each document in the collection, otherwise I will run into the "Error: Not permitted. Untrusted code may only update documents by ID. [403]". But how can I get that? Or should I just make it easy and use collection.allow()? Or is that the only way?
I think you are missing two things:
you need to pass the option, {multi: true}, to update or it will only ever change one record.
if you only want to change some fields of a document you need to use $set. Otherwise update assumes you are providing the complete new document you want and replaces the original.
So I think the correct function is:
Players.update({},{$set: {score: Math.floor(Random.fraction()*10)*5}}, {multi:true});
The documentation on this is pretty thorough.