I am building some little log reporting with meteor.
have a dead simple table, that should contain all the data that received from the external mongodb server.
the html:
<tbody>
{{#each impressions}}
<tr>
{{> impression}}
</tr>
{{/each}}
</tbody>
js :
Meteor.subscribe('impressions');
...
...
Template.logResults.helpers({
'impressions': function() {
var sTs = Router.current().params.fromTs;
var eTs = Router.current().params.toTs;
return Impressions.find({});
}
});
So far, so good. BUT, when I am changing the query to this one :
Impressions.find({
$and: [{
ts: {
$gte: sTs
}
}, {
ts: {
$lte: eTs
}
}]
});
The results aren't displayed on the HTML DOM,
I tried to debug that, and created a console.log of this exact query,
and surprisingly all the correct results return successfully to the console.
screenshot attached.
I am probably doing something wrong, maybe with publish/subscribe thing.
help someone?
Thanks.
P.S. I removed the insecure and auto-publish,
have this code on the server folder
Meteor.publish('impressions', function() {
return Impressions.find();
});
and this code on the main lib folder
Impressions = new Mongo.Collection("banners");
enter image description here
The router stores the parameters for the current route as strings (which makes sense because URLs are strings), so you need to explicitly convert the values to integers before querying the database. Give something like this a try:
var sTs = Number(Router.current().params.fromTs);
var eTs = Number(Router.current().params.toTs);
Notes:
parseInt or parseFloat may be a better choice depending on the nature of your input. See this question for more details.
You could do the type conversion in the router itself and pass the values in the data context to the helpers. See this section of the iron router guide.
I suspect it worked when you typed it into the console because you used numbers instead of strings. E.g. ts: {$gte: 123} instead of ts: {$gte: '123'}.
Related
I want to update only the name field, the problem with the code I have is that if I update a document, all the mongo documents are updated.
As I update a document in specific, I must admit that I am new to this mongo so any help I thank you.
Client
updatename.html
<template name="updatename">
<form class="editor-container">
<input class=“save” type="text" id="card" value=“{{name}}”>
<button type="button" class=“save” id="save">save</button>
</form>
</template>
updatename.js
Template.updatename.events({
'click .save’: function (e, t) {
e.preventDefault();
FlowRouter.watchPathChange();
var name = FlowRouter.current().params._id;
var name = $('#card').val();
Meteor.call('name.update',name);
FlowRouter.go('/');
}
});
Server
name.js
Meteor.methods({
'name.update'( name) {
Name.update({
Name.update({},{ $set: { nam: name }},{multi:true})
});
}
});
I recommend few improvisations over your Meteor code.
Atleast use Title Case/ CamelCase for better readability of the template name and Meteor Methods for other developer.
use submit .formClassName instead of using click .save, also specifiy parameter name with sincerity like function (event, template)
When you updating document for logged user and not other user, as dmayo mentioned in the code use Name.update({_id: Meteor.userId()},{ $set: {nam: name}}), but there is no sense of specifying { multi: true } when you know that there is going to be only 1 record when you are updating. You can use { multi: true } when you desire to impact many records based on criteria that are definitely going to return more than 1 record.
use check(name, String) in Meteor.method call to ensure what you are sending to the server is eligible for further operations.
Use aldeed autoforms when you know there is no out of the box implementation and is going to be simple.
Below is the improvised code for better readability and up to standards
Client
update-name.html
<template name="UpdateName">
<form class="editorContainerForm">
<input type="text" id="card" value=“{{name}}”>
<button type="submit">Save</button>
</form>
</template>
update-name.js
Template.UpdateName.events({
'submit .editorContainerForm’: function (event, template) {
event.preventDefault();
FlowRouter.watchPathChange();
var name = FlowRouter.current().params._id;
var name = $('#card').val();
Meteor.call('updateName',name, function(error, response){
if(error){
// Show some BERT ERROR message for better user experience
// use "meteor add themeteorchef:bert" to add package
} else {
// Show some BERT SUCCESS message for better user experience
});
FlowRouter.go('/');
}
});
Server
name.js
Meteor.methods({
updateName( name ) {
check(name, String);
Name.update({ _id: Meteor.userId },{ $set: { name: name }});
// Use below code only if you know there can be multiple records for same ID
// Name.update({ _id: Meteor.userId },{ $set: { name: name }}, { multi: true });
}
});
In your name.js file (on the server) your mongo query is empty, so when mongo queries your database, it matches all of the documents/records.
Name.update(query, changes, options)
That is the format per the mongo docs. You need to have a unique identifier. Your form is saving a "name", and that's what you are passing to the Meteor.method, but you're not telling the method who's changing their name. If the user is logged in, then you can just use the meteor unique id Meteor.userId()
Name.update({_id: Meteor.userId()},{ $set: {nam: name}},{multi:true})
Also, your option multi:true says to update any and all documents that match the query. If in your original method as written, you had multi:false (the default) then only one document would have been updated (but probably not the one you wanted as the first match would have been updated because of your empty query field.
Mongo docs: https://docs.mongodb.com/manual/reference/method/db.collection.update/
Metor docs: https://docs.meteor.com/api/collections.html#Mongo-Collection-update
I have a Meteor app that has become very unresponsive now that I am trying to use Templates and Template helpers more. I basically have two large collections in MongoDB say collection1 with 1531589 documents and collection2 with 1517397 documents. For each collection, the documents within it adhere to a single schema, i.e. collection1 has it's own schema, and collection2 has it's own schema. Both schemas have only a few properties (~5). In my Meteor code I have taken the following approach
Col1 = new Mongo.Collection('collection1');
Col2 = new Mongo.Collection('collection2');
In publications.js
Meteor.publish('collection1', function() {
if (Roles.userIsInRole(this.userId, 'admin')) {
return Col1.find({"ID": "12345"});
} else {
this.stop();
return;
}
});
Meteor.publish('collection2', function() {
if (Roles.userIsInRole(this.userId, 'admin')) {
return Col2.find({"ID": "12345"});
} else {
this.stop();
return;
}
});
In subscriptions.js
Meteor.subscribe('collection1');
Meteor.subscribe('collection2');
I then have templates that should display data from each collection by means of template helpers, so in essence
<template name="superInfo">
{{#each latestInfo}}
<p>{{latestInfo.prop1}}</p>
<p>{{latestInfo.prop2}}</p>
{{/each}}
</template>
with a template helper
Template.superInfo.helpers({
latestInfo: function() {
return Col1.find({"ID": "12345"}, {
sort: {Date: -1},
limit: 1
});
}
});
I know the fault probably lies with how I am querying/publishing/subscribing and I have read posts like this but I still can't figure out the most efficient way of doing this. Another issue might be how I have configured Mongo locally? I have made sure that there are indexes on each collection for the ID field, but that hasn't helped. Whenever I start my app and browser to the page using these templates it becomes totally unresponsive.
I put a few console.logs in the template helpers and noticed they were getting called quite a lot so that also might be an issue. Any suggestions as to how best to address these issues would be really appreciated.
I'm building a very small Meteor application simply to get a better understanding of Autoform and CollectionFS, and their use together. I currently have everything set up with the following packages:
iron:router, aldeed:autoform, aldeed:collection2, cfs:standard-packages,
cfs:filesystem, cfs:autoform
I have a sample Mongo Collection assigned to "Books" set up with a SimpleSchema attached, with fields from the demo like title and author. The corresponding code for the file upload is:
fileId: {
type: String,
autoform: {
afFieldInput: {
type: "cfs-file",
collection: "images"
}
}
}
The FS.Collection code is:
Images = new FS.Collection("images", {
stores: [new FS.Store.FileSystem("images", {path: "~/uploads"})]
});
This is in conjunction to a quickform: {{> quickForm collection="Books" id="insertBookForm" type="insert"}}
The insert is fine, and I can iterate over the documents and display the various fields using spacebars and a helper function called "books", like so:
{{#each books}}
<li>{{title}} by {{author}}</li>
{{/each}}
I can also iterate over images uploaded to the FS.Collection with a helper returning the entire collection called "files," and looping over them like so:
{{#each files}}
<img src="{{this.url}}" />
{{/each}}
The issue I'm having is linking the two together. I want to be able to do something along the lines of:
{{#each books}}
<li>{{title}}, by {{author}} <img src="The-Corresponding-Image}}" /></li>
{{/each}}
Obviously not that exact layout, but I basically just want to be able to print images with their corresponding titles and authors to be able to use autoform with collectionfs for my needs.
I'm stuck trying to retrieve the fileId from a specific document in the Books collection, and then plugging that into an Images.findOne({fileId: fileId}) and linking the two together.
Can anyone point me in the right direction?
I was able to figure it out thanks to Ethaan's guidance. What I had to do was the following:
Autoform Hook:
AutoForm.hooks({
insertBookForm: {
after: {
insert: function(error, result, template) {
insertedFile = Books.findOne(result).fileId;
Images.update({_id: insertedFile}, {$set: {'book': result}});
}
}
}
});
I set a field of 'book' to the _id of the document being inserted (stored in the result parameter) right after it was inserted.
This is my corresponding HTML:
{{#each books}}
<li>{{title}} by {{author}}</li>
{{#with files}}
<img src="{{this.url}}" />
{{/with}}
{{/each}}
And my helpers:
Template.layout.helpers({
books: function () {
return Books.find({});
},
files: function() {
return Images.findOne({book: this._id});
}
});
As I am new to programming and Meteor I am currently building a (simple) Quizz app using Meteor.js. I followed the discover Meteor Guide book and rebuilding their example microscope project into a quiz. I am currently struggling with retrieving the array of questions from the mongo db and displaying just one of them within the app.
The data within my collection currently looks like this:
Quizzes.insert(
{"quiztitle": "Quiz One",
"quizquestions": ["Q1.1", "Q1.2"]
}),
I am currently displaying all of them thorugh
<template name="quizPage">
<h3>
{{#each quizquestions}}
{{> quizQuestion}}
{{/each}}
</h3>
and
<template name="quizQuestion">
<div class="quiz">
<div class="quiz-content">
{{this}}
</div>
</div>
I have tried several solutions already to getting only the first question to display:
1.Substituting the array number through a helper function with Spacebars. Although the helper worked (it returned a specific number for instance 0), and the array by itself ( 0 between brackets). Meteor does not seem to allow spacebar inserts into array brackets.
<template name="quizQuestion">
<div class="quiz">
<div class="quiz-content">
{{quizquestions.[{{questionnumber}}]}}
</div>
</div>
2.aReturning a specific field through a mongodb query. for example
Return Quizzes.find( { quiztitle: 'Quiz One' }, { quizquestion: 1, _id:0, quiztitle: 0 });
Unfortunately this is only allowed on the server side. I have also tried to save the array resulting from the return into a global variable within the lib folder
questionArray = Quizzes.find( { quiztitle: 'Quiz One' }, { quizquestion: 1, _id:0, quiztitle: 0 } );
This is also the case when I try slicing the collection, which is suggested in a different post
3.As this also does not seem to work I have tried publishing a subset of the collection for use in a specific quiz. The problem I have here is that the collection seems to be published in its entirety. I publish the collection on the frontpage.js through
Meteor.subscribe('quizzes');
I have also tried subscribing within an autorun as is suggested in the documentation at http://docs.meteor.com/#meteor_subscribe
Deps.autorun(function () {
Meteor.subscribe("quizzes")});
Question: Could you help me find a way to return only the questions array and either save it to a global variable or return it through a helper. I hope you can help me out, thanks a lot,
Meteor Beginner.
First, you need to make sure the data is available on the client. In chrome, open up your javascript console (cmd+option+j) and paste Quizzes.find().fetch() and you should see your objects. Assuming that is good, continue...
To get your questions to display, you can return the specific question to a {{#with}} block like this:
{{#with question}}
<li>{{this}}</li>
{{/with}}
Your question helper could look something like this...
Template.TEMPLATE_NAME.helpers({
question: function(){
var currentQuestion = Session.get('currentQuestion') || 0;
return Quizzes.findOne({}).quizquestions[currentQuestion]
}
});
Then you can increment the Session variable each time you answer or go to the next question in a Meteor event, something like this:
Template.TEMP_NAME.events({
'click .next-question': function(){
var num = Session.get('currentQuestion') + 1;
Session.set('currentQuestion', num);
}
});
This will cause the helper to rerun and your new question will be passed back to the {{#with}} block.
In the quizPage template, add a helper which finds a single question:
Template.quizPage.firstQuestion = function() {
return this.quizquestions[0];
}
Then use it:
<template name="quizPage">
<h3>
<!-- #with is like #each, but for a single item -->
{{#with firstQuestion}}
{{> quizQuestion}}
{{/with}}
</h3>
</template>
I use 0.6.4. Don't work Collection.update(t.data._id, { $set: { name: e.currentTarget.value}}); Session.set("edit-" + t.data._id, false);.
I'd recommend using jQuery to extract the value: $(e.currentTarget).val(). Also, assuming the person template is rendered from an {{#each people}}, you could probably just do this._id, but it's hard to tell without seeing the templates.
People.update(this._id, { $set: { name: $(e.currentTarget).val()}});
I'd also suggest logging these values to the console before the update to make sure the callback is getting executed and that you are reading the right values.