In meteor framework inside pre-added code, the counter increases every time when it gets clicked. How to save the value using mongodb ?
Create a collection on the server side to persist the data:
Meteor.isServer {
Counter= new Mongo.Collection('Counter');
// Server side method to be called from client
Meteor.methods({
'updateCounter': function (id) {
if(typeof id && id) {
return Counter.update({_id: id}, {$set: {counter: {$inc: 1}}});
} else {
return Counter.insert({counter: 1})
}
}
})
// Publication
Meteor.publish("counter", function () {
Counter.find();
})
}
You can subscribe the data at the client:
Meteor.isClient{
Template.yourTemplateName.created = function () {
Meteor.subscribe('counter');
}
Template.yourTemplateName.heplers( function () {
counter: function () {
return Counter.findOne();
}
})
Template.yourTemplateName.event( function () {
'click #counterButtonIdName': function () {
if(Counter.findOne()) {
Meteor.call('updateCounter', Counter.findOne()._id);
} else {
Meteor.call('updateCounter', null);
}
}
})
}
Html sample
<template name="yourTemplateName">
<span>{{counter}}</span> //area where count is written
</template>
By this way you can achieve a secure server side processing of your data and the count will be persistent until you have data in the database. Also, this way you can learn Meteor's basics.
Just insert it to a collection. Here's an upsert (i.e., update if exists, insert if not) function:
if (Saves.find({_id: Meteor.userId()})){
Saves.update( {_id: Meteor.userId()}, {save: save} )
console.log("Updated saves")
}
else {
Saves.insert(save)
}
If the autopublish package exists, you can simply create a Mongo.Collection and insert this counter into the database:
var myCounter = 5;
var collection = new Mongo.Collection('collection');
collection.insert({counter: myCounter});
Hope this helps.
Related
I am having difficulties with meteor 1.6. First, I have created a database instance and tried to subscribe in the client. through form, I am able to input the data into my database. But could not retrieve it through the subscribe. can anybody tell me what wrong have I done in my code?
import { Template } from "meteor/templating";
import { Notes } from "../lib/collection";
import { Meteor } from "meteor/meteor";
// import { ReactiveDict } from 'meteor/reactive-dict';
import "./main.html";
/*
Template.body.onCreated(function bodyOnCreated() {
this.state = new ReactiveDict();
Meteor.subscribe("db1");
}); */
Template.Display.helpers({
notes() {
Meteor.subscribe("db1");
return Meteor.call('data');
}
});
Template.body.events({
"click .delete": function() {
Notes.remove(this._id);
},
"submit .formSubmit": function(event) {
event.preventDefault();
let target = event.target;
let name = target.name.value;
Meteor.call("inputs", name);
target.name.value = "";
return false;
},
"click .userDetail": function() {
if (confirm("Delete the user Detail ?")) {
Notes.remove(this._id);
}
}
});
here is the code for publication :
import { Mongo } from 'meteor/mongo';
export const Notes = new Mongo.Collection('notes');
Meteor.methods({
inputs:(name)=> {
if (!Meteor.user()) {
throw Meteor.Error("Logged in");
}
Notes.insert({
name: name,
createdAt: new Date()
});
},
data:()=>{
return Notes.find({});
}
});
Meteor.subscribe("notes"); should be in Template.body.onCreated lifycycle method. you need to write a
publish code seperately and not inside the Meteor.method. see below format,
Meteor.publish('notes', function tasksPublication() {
return Notes.find({});
});
Inside the helper just call the subscribed Collection a below,
Template.Display.helpers({
notes() {
return Notes.find({});
}
});
**NOTE: ** Never use Meteor.call inside the helper method. helpers are reactive and real time.
I dynamically create collections with this method:
createPlist: function(jid) {
try {
Plist[jid] = new Meteor.Collection(pid);
} catch(e) {
console.log("oops, I did it again");
}
Plist[jid].insert({
...,
...,
public:true,
uid:this.userId
});
}
Then I am wanting to publish these selectively, and I am attempting to do it via a method:
getPlist: function(jid,pid) {
// var future = new Future();
try {
Plist[jid] = new Meteor.Collection(pid);
} catch(e) {
console.log("oops, I did it again");
}
Meteor.publish(pid, function() {
console.log(Plist[jid].find({}));
// future["return"](Plist[jid].find({}));
return Plist[jid].find();
});
// return future.wait();
},
This returns 'undefined' to my Template helper, and returns nothing (i.e. waits forever) using Future.
Any user can log in and create a Plist collection, which can be either public or not. A user can also subscribe to any collection where public is true. The variable jid is passed to the method 'getPlist' from the template. It is stored in the user's Session.
Thanks! I hope I have explained it well enough!
And of course the template:
Template.plist.helpers({
getPlist: function() {
Pl = []
jid = Session.get('jid');
//alert(jid);
pid = "pl_"+jid;
// console.log(pid);
Meteor.call('getPlist', jid, pid, function(err,res) {
console.log(res); //returns undefined
try {
Pl[jid] = new Meteor.Collection(pid);
} catch(e) {
console.log(e);
}
Meteor.subscribe(pid);
// return Pl[jid].find({}).fetch();
});
}
I'm trying to publish a collection with 2 different names.
freeCourses contains courses without paid_url field.
premiumCourses contains all courses which id exist in userCourses collection.
userCourses collection :
{ user_id: "1", course_id: "1" }
Meteor.publish('freeCourses', function () {
this.added('freeCourses', Courses.find({}, {fields: {'Seasons.Episodes.paid_url': 0}}));
this.ready();
});
Meteor.publish('premiumCourses', function () {
//userPremiumCourses is array of course_ids
var userPremiumCourses = userCourses.find({'user_id': this.userId}, {fields: {course_id: 1, _id: 0}}).map(
function (doc) {
return doc.course_id;
}
);
this.added('premiumCourses', Courses.find({_id: {$in: userPremiumCourses}}));
this.ready();
});
if(Meteor.isClient){
Meteor.subscribe('freeCourses');
Meteor.subscribe('premiumCourses');
}
I want to get freeCourses and premiumCourses as two different collections on the client.
I've never seen this done before but if it was possible I believe you would need to define two collections that referred to the same underlying mongo collection:
freeCourses = new Mongo.collection('userCourses');
premiumCourses = new Mongo.collection('userCourses');
I just tested that and that fails.
A collection can have multiple publications each with its own query parameters and fields but it appears you want something more like a SQL view. That doesn't exist in Meteor afaik.
so I used publishVirtual function. thanks to #michel floyd
function publishVirtual(sub, name, cursor) {
var observer = cursor.observeChanges({
added : function(id, fields) { sub.added(name, id, fields) },
changed: function(id, fields) { sub.changed(name, id, fields) },
removed: function(id) { sub.remove(name, id) }
})
sub.onStop(function() {
observer.stop() // important. Otherwise, it keeps running forever
})
}
and added this into publish :
Meteor.publish('freeCourses', function () {
var cursor = Courses.find({}, {fields: {'Seasons.Episodes.paid_url': 0}});
publishVirtual(this, 'freeCourses', cursor);
this.ready();
});
Meteor.publish('premiumCourses', function () {
//userPremiumCourses contains array of course_ids
var userPremiumCourses = userCourses.find({'user_id': this.userId}, {fields: {course_id: 1, _id: 0}}).map(
function (doc) {
return doc.course_id;
}
);
var cursor = Courses.find({_id: {$in: userPremiumCourses}});
publishVirtual(this, 'premiumCourses', cursor);
this.ready();
});
and made two client-side collections for subscribe :
if (Meteor.isClient) {
freeCourses = new Mongo.Collection("freeCourses");
premiumCourses= new Mongo.Collection("premiumCourses");
Meteor.subscribe('freeCourses');
Meteor.subscribe('premiumCourses');
}
I wish to use Meteor to subscribe a few remote publication via DDP. Then show the documents in one template. Here is what I did:
Posts = {};
var lists = [
{server: "localhost:4000"},
{server: "localhost:5000"}
];
var startup = function () {
_.each(lists, function (list) {
var connection = DDP.connect(`http://${list.server}`);
Posts[`${list.server}`] = new Mongo.Collection('posts', {connection: connection});
connection.subscribe("allPosts");
});
}
startup();
This file is at client folder. Every startup, in this example, at browser I have two client collections Posts["localhost:4000"] and Posts["localhost:5000"], both are same schema. I know this format (Collection[server]) is ugly, please tell me if there is a better way.
Is there a way to show these client collections in the same template with reactive. Like this:
Template.registerHelper("posts", function () {
return Posts.find({}, {sort: {createdAt: -1}});
});
I think Connected Client is a big part of the Meteor. There should be a best practice to solve this problem, right?
Solved.
Connect to multiple servers via DDP, then observe their collections reactive via cursor.observeChanges.
Posts = {};
PostsHandle = {};
// LocalPosts is a local collection lived at browser.
LocalPosts = new Mongo.Collection(null); // null means local
// userId is generated by another Meteor app.
var lists = [
{server: "localhost:4000", userId: [
"hocm8Cd3SjztwtiBr",
"492WZqeqCxrDqfG5u"
]},
{server: "localhost:5000", userId: [
"X3oicwXho45xzmyc6",
"iZY4CdELFN9eQv5sa"
]}
];
var connect = function () {
_.each(lists, function (list) {
console.log("connect:", list.server, list.userId);
var connection = DDP.connect(`http://${list.server}`);
Posts[`${list.server}`] = new Mongo.Collection('posts', {connection: connection}); // 'posts' should be same with remote collection name.
PostsHandle[`${list.server}`] = connection.subscribe("posts", list.userId);
});
};
var observe = function () {
_.each(PostsHandle, function (handle, server) {
Tracker.autorun(function () {
if (handle.ready()) {
console.log(server, handle.ready());
// learn from http://docs.meteor.com/#/full/observe_changes
// thank you cursor.observeChanges
var cursor = Posts[server].find();
var cursorHandle = cursor.observeChanges({
added: function (id, post) {
console.log("added:", id, post);
piece._id = id; // sync post's _id
LocalPosts.insert(post);
},
removed: function (id) {
console.log("removed:", id);
LocalPosts.remove(id);
}
});
}
})
});
}
Template.posts.onCreated(function () {
connect(); // template level subscriptions
});
Template.posts.helpers({
posts: function () {
observe();
return LocalPosts.find({}, {sort: {createdAt: -1}}); // sort reactive
}
});
I'm writing a small application that shows live hits on a website. I'm displaying the hits as a table and passing each one to a template helper to determine the row's class class. The idea is that over time hits will change colour to indicate their age.
Everything renders correctly but I need to refresh the page in order to see the helper's returned class change over time. How can I make the helper work reactively?
I suspect that because the collection object's data isn't changing that this is why and I think I need to use a Session object.
Router:
Router.route('/tracked-data', {
name: 'tracked.data'
});
Controller:
TrackedDataController = RouteController.extend({
data: function () {
return {
hits: Hits.find({}, {sort: {createdAt: -1}})
};
}
});
Template:
{{#each hits}}
<tr class="{{ getClass this }}">{{> hit}}</tr>
{{/each}}
Helper:
Template.trackedData.helpers({
getClass: function(hit) {
var oneMinuteAgo = Date.now() - 1*60*1000;
if (hit.createdAt.getTime() > oneMinuteAgo) {
return 'success';
} else {
return 'error';
}
}
});
I've managed to get this working though I'm not sure it's the 'right' way to do it. I created a function that is called every second to update Session key containing the current time. Then, in my helper, I can create a new Session key for each of the objects that I want to add a class to. This session key is based upon the value in Session.get('currentTime') and thus updates every second. Session is reactive and so the template updates once the time comparison condition changes value.
var updateTime = function () {
var time = Date.now();
Session.set('currentTime', time);
setTimeout(updateTime, 1 * 1000); // 1 second
};
updateTime();
Template.trackedData.helpers({
getClass: function(hit) {
var tenMinutesAgo = Session.get('currentTime') - 10*1000,
sessionName = "class_" + hit._id,
className;
className = (hit.createdAt.getTime() > tenMinutesAgo) ? 'success' : 'error';
Session.set(sessionName, className);
return Session.get(sessionName);
}
});
Update
Thanks for the comments. The solution I ended up with was this:
client/utils.js
// Note that `Session` is only available on the client so this is a client only utility.
Utils = (function(exports) {
return {
updateTime: function () {
// Date.getTime() returns milliseconds
Session.set('currentTime', Date.now());
setTimeout(Utils.updateTime, 1 * 1000); // 1 second
},
secondsAgo: function(seconds) {
var milliseconds = seconds * 1000; // ms => s
return Session.get('currentTime') - milliseconds;
},
};
})(this);
Utils.updateTime();
client/templates/hits/hit_list.js
Template.trackedData.helpers({
getClass: function() {
if (this.createdAt.getTime() > Utils.secondsAgo(2)) {
return 'success';
} else if (this.createdAt.getTime() > Utils.secondsAgo(4)) {
return 'warning';
} else {
return 'error';
}
}
});