Meteor run a function when a mongo collection gets updated - meteor

I am trying to do a simple prototype using Meteor (I am very new to Meteor).
I have the following in an isClient block
Template.imageList.helpers({
images: function() {
return Images.find({});
},
}
);
Then for quick demo purposes I am inserting data with meteor mongo and the following
db.images.insert({ imgSrc: "http://mysite/img1.png", createdAt: new Date() })
This works and I can see the change reflected on the ui but I need to also run a client side javascript function when this occurs. I have been trying things like pub/sub and Tracker but can't get anything to work.
Any guidance would be great.

Using meteor-collections-hooks its the more easy way to accomplish this.
meteor add matb33:collection-hooks
For example.
Images = new Mongo.Collection('Images')
example = function(){
console.log("updated")
}
if (Meteor.isClient) {
Images.before.update(function(userId, doc, fieldNames, modifier, options){
example();
})
}
the example() function will run each time the Images collection get updated.

Use observeChanges :
Images.find().observeChanges({
added: function (id, fields) {
runFunction();
},
changed: function (id, fields) {
runFunction();
},
removed: function (id) {
runFunction();
}
});
See here for more: http://docs.meteor.com/#/full/observe_changes

Run function on the client's template helper, like:
Template.imageList.helpers({
images: function() {
imgs = Images.find({});
yourFunction();
return images;
}
});
Or use Tracker.autorun wrapper:
Tracker.autorun(
Images.find({});
yourFunction();
)

Related

Vuejs & Firestore - How to Update when Data Changes in Firestore

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!

Ionic and SQLite wait until database info is loaded

I have a project on Ionic where I need to render some information of the database in the home page, something like a TODO program.
I already have some information on the database and I'm trying to render the list of items but I have the next problem:
First the home page is loaded without any result
Then the data from the database is loaded and printed on the screen
The problem is I want the view to wait until the database info is loaded until showing anything, I'm wondering if I can use some kind of loading icon.
I've followed the answer here: Open database before main controller is called in Ionic and SQlite
I have the database initialization working but as I've said, the data is loaded after the view is rendered.
I've tried using $ionicLoading but I didn't get any good result
This is my view:
.controller('homeCtrl', function ($scope, $state, $cordovaSQLite, DB) {
$scope.$on('$ionicView.enter', function() {
tasks = []
var query = "SELECT * FROM task;";
$cordovaSQLite.execute(DB.db, query, []).then(function(results) {
if(results.rows.length > 0) {
for (i=0; i<results.rows.length; i++){
console.log("SELECTED -> " + results.rows.item(0).title);
$scope.tasks.push(results.rows.item(i))
}
} else {
console.log("No results found");
}
}, function (err) {
$scope.tasks = [];
console.error(err);
});
$scope.tasks = tasks;
});
})
This is a video example of the issue I'm having right now:
https://youtu.be/H2fUYQuV3xg
Finally I found a solution following the advice of using resolve in my routes.
.state('home', {
url: '/',
templateUrl: 'templates/home.html',
controller: 'homeCtrl',
resolve: {
tasks: function(DB) {
return DB.getTasks();
});
}
}
})
I have a factory called DB where I have some functions to retrieve data from the database. On this example I load the tasks before entering on the URL using DB.getTasks()
To load the variable tasks resolved on the route I have to add the variable name on the function like this:
app.controller('homeCtrl', function (tasks) {
$scope.tasks = tasks;
})

Meteor Geolocation method from Event

I want to get the latLng, but only after an event. How can this be achieved? I've tried tracker and the like but nothing has worked. The only thing that has worked is calling Geolocation.latLng(); from inside the helper, which is before the event.
Here is how I wish it would work. I've tried the same thing with Session.set() and Session.get()). I've also tried to use Tracker dependencies, but since the location isn't available immediately triggering the changed() doesn't help.
I should include that I'm using the package created by the Meteor Development Group located at https://github.com/meteor/mobile-packages/.
var location = {};
Template.Home.helpers({
'location': function() {
return location;
}
);
Template.Home.events({
'focus .location': function() {
location = Geolocation.latLng();
},
});
I like #ZuzEL's answer, but in case you really want to do it your way with Sessions:
Template.Home.helpers({
'location': function() {
return Session.get("location");
}
);
Template.Home.events({
'focus .location': function() {
Session.set("location", Geolocation.latLng());
},
});
no need for the ReactiveVar package because Sessions are like global reactive themselves :)
This is because your location is not reactive variable itself.
var location = new ReactiveVar();
Template.Home.helpers({
'location': function() {
return location.get();
}
);
Template.Home.events({
'focus .location': function() {
location.set(Geolocation.latLng());
},
});
Don't forget to include reactive var package
meteor add reactive-var
But, since you are using mdg:geolocation
And here API Doc says that every method is reactive, you can use tracker in onRendered callback whatever the location changes
Template.Home.onRendered(function(){
this.autorun(function(){
location.set(Geolocation.latLng());
})
});

Meteor: Publishing all user not working without autopublish package

I would to show a list of all users, in my template.
I have:
//publications.js
Meteor.publish('users', function() {
return Meteor.users.find({}, { fields: {username: 1, profile: 1} });
});
//router.js
Router.route('/users/add/:_id?', {name: 'users.add', controller: 'UserAddController'});
UserAddController = RouteController.extend({
subscriptions: function(){
return [ Meteor.subscribe('hospitals'),
Meteor.subscribe('roles'),
Meteor.subscribe('users') ];
},
action: function() {
this.render('addUser', {
data: function(){
return { hospital_id : this.params._id }
}
});
}
});
//client
Template.listUsers.helpers({
users: function() {
return Meteor.users.find({});
}
});
But the list keep showing only the current logged-in user. I have created a list of users using Account.createUser() function What am I doing wrong?
Thanks.
You have to subscribe to a publication using this.subscribe() in subscriptions hook:
// a place to put your subscriptions
subscriptions: function() {
this.subscribe('items');
// add the subscription to the waitlist
this.subscribe('item', this.params._id).wait();
}
Or use waitOn:
// Subscriptions or other things we want to "wait" on. This also
// automatically uses the loading hook. That's the only difference between
// this option and the subscriptions option above.
waitOn: function () {
return Meteor.subscribe('post', this.params._id);
}
By default, Meteor publishes the current user. I see that you have a addUser template and a listUsers template. The problem is that while addUser is subscribed to the users publication, listUsers is not (this would depend on what else you have in your router of course). To fix this, change the call to this.render to render the listUsers template. Then, your users helper should work, and you can render the information however you like.
I tested this with my own app (the Microscope project from DiscoverMeteor) and it worked for me. Hope it works for you too. Comment here if not, and be sure to accept this answer if it worked. =)

Meteor Iron Router Run function when collection changes

Im new to Meteor and Im trying to figure out how to run a function after a collection change.
I have a route(iron router) that subscribes to a collection with waitOn. Which just waits for the subscrition to be ready before rendering which is what I want.
waitOn: function () {
return Meteor.subscribe('collection', this.params._id);
},
Any changes to the collection will be updated on all the clients and rendered automatically.
How would I run a function once the collection has changed?
You can use the onData hook, provided that you're returning that data using the data helper. E.g this is what a route may look like
this.route('routename',
path : '/abc',
waitOn: function () {
return Meteor.subscribe('collection', this.params._id);
},
data: function() {
return Collection.findOne({_id: this.params.id});
}
onData: function() {
//Do something when the data found by the above changes
}
});

Resources