I have a courses collection
{courses{{course1_title,id,author,pages[{title,content,model, etc...},
[ etc...]{course2,....}}
I'm trying to display the current page's data inside the courses collection through the router
this.route('page', {
path: '/:title',
data: function() {
return courses.findOne({'pages.title':this.params.title};
}
});
I would like to display the current page's data like this:
<template name="page">
<div class ="container page">
<h1>Page</h1>
<!--display current page data -->
<h3>{{title}}</h3>
<div>{{ an other content}} etc..... </div>
</div>
For now, the router returns the entire course's data and the title displayed is the course's title. I don't find how to access the current page's data in order to display it in the page's template.
I tried
return courses.findOne({'pages.title':this.params.title}{fields:{{'pages.title':this.params.title}:1}}
and a lot of other ways. I didn't find it.
What is the right way?
Your current query will search all the courses for a matching page title, then return the entire course (with all the pages)
You should return only the data for the page in question:
course = courses.findOne({pages: { $elemMatch: { title: this.params.title }});
return course.pages[0]
As an aside it's probably better to create a separate collection for pages (with each page linking back to the course id). Although that's less "Mongo", Meteor can only operate reactively over collections, not sub-documents.
Related
I have a collection of People that has the fields {name, id}
I also have the field Likes that contains {user_id, foodName}
I want to create a template that will display a list of People, and the food they like. I am running an each in my template to pull all of this information from a query on the Likes collection. I want to take the queried user_id from Likes and then use it to pull the associated name of the person from the People collection.
My template looks like this:
<template name="likes">
{{#each likes_list}}
//This displays the user_id, but I want to have it display the user's name
<p>{{user_id}} likes {{foodName}}</p>
{{/each}}
</template>
My helper looks like this:
Template.likes.helpers({
likes_list: function() {
return Likes.find();
}
});
I see 2 solutions:
Add userName filed to your Likes collection and display it like
this {{userName}} in your template
Create publish function which will "join" your collections (Users and Likes). To do it use this package: https://atmospherejs.com/reywood/publish-composite
You can add a helper to return the user data context and then use it with with:
html:
<template name="likes">
{{#each likes_list}}
<p>{{#with person}}{{name}}{{/with}} likes {{foodName}}</p>
{{/each}}
</template>
js:
Template.likes.helpers({
likes_list: function() {
return Likes.find();
},
person: function(){
return People.findOne({id: this.user_id});
}
});
You could map it into a single object if you wanted to.
Template.blah.helpers({
likes: function () {
return Likes.find().map(function (doc) {
return _.extend(doc, {
user: Meteor.users.findOne(doc.user_id)
});
});
}
});
This will add the user field to the collection for you to reference
{{#each likes}}
{{user.name}} likes {{foodName}}
{{/each}}
So I'm new to Meteor, and have been playing around with it, but have hit a few issues with moving over to Mongo.
Below is a simple example, where I have inserted some documents into my collection, but cannot seem to extract the attributes properly.
Here is my code (and my result vs expected result is below it)
vizart.js
ContentPieces = new Mongo.Collection("content");
if (Meteor.isClient) {
Template.loggedInDash.helpers({
content: function () {
return ContentPieces.find({});
}
});
}
vizart.html
<template name="loggedInDash">
{{#if currentUser}}
<p>Here is the content you've created</p>
<ul>
{{#each content}}
<li>{{content}}</li>
{{/each}}
</ul>
{{/if}}
</template>
Result (just pasted from the app in my browser)
Here is the content you've created
[object Object]
[object Object]
[object Object]
Expected
As you can see, I am not 100% sure how to pull out an attribute. For example, each document has a name attribute, and I'd like to spit that out in the list instead. Any help or guidance on how to select the name attribute from the content collection?
Thanks in advance!
The meteor template language (spacebars) is inspired by handlebars. I'd recommend having a look at both sets of docs, but the handlebars documentation will get you up to speed with the basic syntax.
In your example, if each document in ContentPieces has a name then you can add it to your list like this:
<ul>
{{#each content}}
<li>{{name}}</li>
{{/each}}
</ul>
I'd also recommend having a look at this post to better understand template data contexts.
In your process you are displaying the whole document, if you just want to display the name attribute you can do it by
if (Meteor.isClient) {
Template.loggedInDash.helpers({
content: function () {
var name=ContentPieces.find({}).name;
if(name)
return name;
}
});
You can just pass the field name that you want to display
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 am new to Mongo and NoSQL databases. Can someone explain the way to do a one to many join and a cycling through collections in Meteor.
For example, say I have two collections, a Post and a Comment where each comment has a postId, meaning each Post has zero or many Comments. I am interested in what would be best practice for this type of situation for Meteor specifically where you can cycle through each post and comment in a nested Handlebars call. Something like the example below:
{{#each post}}
{{title}}
{{content}}
{{#each comment}}
{{comment_text}} by {{author}}
{{/each}}
{{/each}}
Although the standard MongoDB paradigm is to denormalize data, in Meteor applications it's not uncommon to stick to the pattern of having different collections (tables) for each logical dataset.
To implement joins in Meteor webapps, you simply have to define a relation between the two collections :
var postId = Posts.insert({
title: "A post",
content: "Some content..."
});
Comments.insert({
postId: postId,
author: "Someone",
text: "Some text..."
});
Denormalizing means that you must not forget to publish the two collections, you can do as follow :
Meteor.publish("postById", function(postId){
// publish the according post...
var postCursor = Posts.find(postId);
// ...and every comments associated
var commentsCursor = Comments.find({
postId: postId
});
// you can return multiple cursors from a single publication
return [postCursor, commentsCursor];
});
This publication would send down to the client a post and all its comments, given a post._id.
Associated with correct client-side routing, you can subscribe to this publication with the post id retrieved from a URL (/posts/:_id) and display the post with all its comments.
Your template pseudo code is OK, however I would refactor it using a distinct template for each collection.
HTML
<template name="outer">
{{!-- loop through each post, the child template will
be using the current post as data context --}}
{{#each posts}}
{{> post}}
{{/each}}
</template>
JS
Template.outer.helpers({
posts: function(){
return Posts.find();
}
});
HTML
<template name="post">
<h3>{{title}}</h3>
<p>{{content}}</p>
{{!-- loop through each comment and render the associated template --}}
{{#each comments}}
{{> comment}}
{{/each}}
</template>
JS
Template.posts.helpers({
comments: function(){
// return every comment belonging to this particular post
// here this references the current data context which is
// the current post being iterated over
return Comments.find({
postId: this._id
});
}
});
HTML
<template name="comment">
<p>{{text}}</p>
<span>by {{author}}</span>
</template>
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Mongoose - Form to save model with embedded documents
I have a simple Mongoose schema with BlogPosts and Feeds embedded within those BlogPosts. So each BlogPost can have several Feed objects. Right now my HTML form allows me to correctly save one 'Feed' when I create a BlogPost, BUT I would like to be able to add many Feeds. How should I change my HTML/controller? Thanks much!
web.js
app.post('/blogpost/new', function(req, res){
var post = new BlogPost(req.body.post)
post.feeds.push(req.body.feed);
post.save(function() {
res.redirect('/blogposts');
});
});
/blogpost/new
<form method="post">
<input type="text" name="blogpost[title]"/>
<textarea name="feed[name]"></textarea>
<textarea name="feed[key]"></textarea>
<textarea name="feed[name]"></textarea>
<textarea name="feed[key]"></textarea>
</form>
schema
var Feed = new Schema({
name : { type: String }
, key : { type: String }
});
var BlogPost = new Schema({
title : { type: String, required: true, index: { unique: true } }
, feeds : [Feed]
});
How do I make it so that this form stores two 'Feeds' in the blogpost object it creates? right now it would save One Feed in the data with two name values and two key values.
Thanks much!
When you do something like this:
<textarea name="feed[name]"></textarea>
<textarea name="feed[key]"></textarea>
<textarea name="feed[name]"></textarea>
<textarea name="feed[key]"></textarea>
You are basically saying "I need a textbox for feed's name, feed's key, feed's name, feed's key which is obviously incorrect. If you did feed[0][name]...feed[1][name] that would say "i need a textbox for the first feed...i need a textbox for the second feed"
Since you are breaking the parent and child up, rather than handling them as sub objects, you'll need to push each into the blogPost.feeds and save it.
Just be careful with that approach, especially when editing, because you are simply adding new objects ontop of what could already be an existing array of feeds.