Limiting each inside of each based on context - meteor

Having trouble trying to pass some sort of context into my little test chan-clone.
Problem is I have threads, with replies. Showing just the threads works great, showing threads with replies based upon the threads they come from not so much.
Question is how do I send context on the outer each. Thank you so much for the help!
meteorchan.html
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
<h1>Meteor Chan - Let's do this</h1>
<form class="new-thread">
<input type="text" name="text" placeholder="New Thread" />
<button>Submit</button>
</form>
<br/>
{{#each threads}}
{{> thread}}
{{#each replies}}
{{> reply}}
{{/each}}
{{/each}}
</body>
<template name="thread">
<div class="thread">
<span>Anonymous</span> No. {{iden}}
<br>
{{text}}
</div>
<a class="reply" href="#">Reply</a> : <button class="purge">Purge</button>
<form class="new-reply {{this._id}}" style="display:none;">
<input type="text" name="text" placeholder="Add Reply" />
<input type="text" name="thread" value="{{this._id}}" style="display:none;" />
<button>Add New Reply</button>
</form>
<br>
<br>
</template>
<template name="reply">
<div class="reply">
{{text}}
<br>
{{parentThread}}
</div>
</template>
meteorchan.js
Threads = new Mongo.Collection("threads");
Replies = new Mongo.Collection("replies");
if (Meteor.isClient) {
Meteor.subscribe("threads");
Template.body.helpers({
threads: function () {
return Threads.find({}, {sort: {createdAt: -1}});
},
replies: function () {
return Replies.find({}, {sort: {createdAt: 1}});
}
});
Template.body.events({
"submit .new-thread": function (event) {
event.preventDefault();
var text = event.target.text.value;
var currentId = 0;
if(Threads.findOne({}, {sort: {createdAt: -1}})){
currentId = Threads.findOne({}, {sort: {createdAt: -1}}).iden;
}
Threads.insert({
text: text,
createdAt: new Date(),
iden: currentId + 1
});
event.target.text.value = "";
},
"submit .new-reply": function (event) {
event.preventDefault();
var text = event.target.text.value;
var thread = event.target.thread.value;
Replies.insert({
text: text,
createdAt: new Date(),
parentThread: thread
});
event.target.text.value = "";
}
});
Template.thread.events({
"click .purge": function (){
Threads.remove(this._id);
},
"submit .new-reply": function(event){
event.preventDefault();
},
"click .reply": function(){
$('.'+this._id+'.new-reply').css('display','inherit');
}
});
}
if (Meteor.isServer) {
Meteor.publish("threads", function () {
return Threads.find();
});
Meteor.publish("replies", function () {
return Replies.find();
});
}

You can refer to the context in the template helper using this.
So in your replies helper this refers to the current thread you are iterating over using the each.
You can do the following to only fetch replies for the corresponding thread:
replies: function () {
return Replies.find({
// this refers to the current thread
parentThread: this._id
}, {sort: {createdAt: 1}});
}

Related

insert new doc via autoform hooks call meteor method

I want to insert a new document into db using autoform. Autoform hook calls a meteor method on server to insert the document.
I have this in template...
{{#autoForm collection="Reports" id="addReport" type="insert"}}
<div class="row">
<div class="col s6">
{{> afQuickField name='hours'}}
</div>
</div>
<button class="btn waves-effect waves-light modal-action modal-close"><i class="material-icons">save</i></button>
{{/autoForm}}
Then...
AutoForm.hooks({
addReport: {
onSubmit: function(insertDoc) {
Meteor.call('addReport', insertDoc, function(error, result) {
if (error) alert(error.reason);
});
return false;
}
}
});
then the method on server...
Meteor.methods({
addReport: function(insertDoc) {
var report = _.extend(insertDoc, {
userId: Meteor.userId(),
});
return Reports.insert(report);
}
});
I have a createdAt and updatedAt fields in collection, but they all have autoValue thus, I believe no need to do insertion from client or in the meteor method.
So collection with schema looks like this:
Reports = new Meteor.Collection('reports');
Reports.attachSchema(new SimpleSchema({
hours: {
type: Number,
label: "Number of hours",
decimal: true
},
createdAt: {
type: Date,
label: "Created Date",
autoValue: function() {
if (this.isInsert) {
return new Date;
} else {
this.unset();
}
},
denyUpdate: true
},
updatedAt: {
type: Date,
autoValue: function() {
if (this.isUpdate) {
return new Date()
}
},
denyInsert: true,
optional: true
},
"userId": {
type: String,
autoform: {
type: "hidden",
}
},
}));
When i run meteor, form displays, but submit does nothing. No visual cue as to if any error. No error message in both client and server console.
what am I doing wrong or missing?
Since #Cristo GQ got it right, I just want to make sure the answer is clear enough for future visitors of this thread
The onSubmit hook will be used only for autoForms with type='normal' or without any type= at all
On another note, the before.insert hook is just for type='insert' And there is no before.normal hook
This mean that when using the onSubmit hook, we have to do any "before work" (like adding currentUser to the doc) inside the onSubmit itself.
aldeed/meteor-autoform documentation:
// Called when form does not have a `type` attribute
onSubmit: function(insertDoc, updateDoc, currentDoc) {
Meteor.call()...
}
I'm following discovermeteor book, and I'm trying to use some methods of the book, but using the meteor-autoform package.
post_submit.html
<template name="postSubmit">
{{#autoForm collection="Posts" id="insertPost"}} <-- no type
<div class="form-group">
<div class="controls">
{{> afQuickField name='title' class='title form-control'}}
</div>
</div>
<div class="form-group">
<div class="controls">
{{> afQuickField name='description' class='description form-control'}}
</div>
</div>
<input id="send" type="submit" value="Send" class="btn btn-primary"/>
{{/autoForm}}
</template>
post_submit.js
var postSubmitHook = {
onSubmit: function(insertDoc){
Meteor.call('postInsert', insertDoc, function(error, result) {
if (error){
Bert.alert(error.reason, 'danger', 'growl-top-right');
$('#send').removeAttr('disabled');
return;
}
Router.go('postPage', {_id: result._id});
});
return false;
}
};
AutoForm.addHooks('insertPost', postSubmitHook);

Having issues with showing collection data in meteor

I am having collection issues in my Meteor project. I added the following code to my Resolutions.js file:
Resolutions = new Mongo.Collection('resolutions');
if (Meteor.isClient) {
Template.body.helpers({
resolutions: function() {
Resolutions.find({});
}
});
Template.body.events({
'submit .new-resolution': function(event) {
var title = event.target.title.value;
Resolutions.insert({
title: title,
createdAt: new Date()
});
event.target.title.value = "";
return false;
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
and added the following to my Resolutions.html file:
<head>
<title>Resolutions</title>
</head>
<body>
<div class="container">
<header>
<h1>Monthly Resolutions</h1>
<form class="new-resolution">
<input type="text" name="title" placeholder="A new resolution" />
<input type="submit" value="Submit"/>
</form>
</header>
<ul>
{{#each resolutions}}
{{> resolution}}
{{/each}}
</ul>
</div>
</body>
<template name="resolution">
<li>{{title}}</li>
</template>
After running the app I get zero errors but still the site does not return any of the collection value. I have no clue what is wrong with this project.
Template.body.helpers({
resolutions: function() {
return Resolutions.find({});
}
});
You forgot to use the return keyword.Remember that when creating a helper, you always need to return a value.

Meteor: collection empty after insert

I am new to meteor and got stucked and can't understand what am I doing wrong. Enlighten me please.
Here is the HTML file:
<body>
<h1>Do</h1>
{{#if activeTask}}
{{> currentTask}}
{{else}}
{{> newTask }}
{{/if}}
<div>
</div>
</body>
<template name="newTask">
<form>
<label>What<input type="text" name="what" placeholder="gimme an action"/></label>
<input type="submit" value="Go"/>
</form>
<!--
{{> inputAutocomplete settings=settings id="msg" class="input-xlarge" placeholder="action"}}
-->
</template>
<template name="currentTask">
<form>
<label>What<input type="text" name="what" placeholder="gimme an action"/>{{activeTask.what}}</label>
<div>4h 15m</div>
<input type="submit" value="Stop"/>
</form>
</template>
And here is the JavaScript file:
tasks = new Mongo.Collection('tasks');
if (Meteor.isClient) {
Template.body.helpers({
activeTask: function() {
var task = tasks.findOne(
{
endAt: null
},
{
sort: {
startAt: -1
}
}
);
console.log(task);
return task;
}
});
Template.newTask.events({
'submit' : function(event) {
event.preventDefault();
var now = Date.now();
var what = event.target.what.value;
tasks.insert({ what: what, startAt: now, endAt: null });
}
});
}
It successfully adds a new document into the database and logs this in the helper activeTask. One step later it logs no task at all. It has gone. But why?
If you don't have the package autopublish (https://atmospherejs.com/meteor/autopublish) installed, you need to create a publication (client-side) and subscription (server-side):
if (Meteor.isServer) {
Meteor.publish('tasks', function () {
return tasks.find();
});
}
if (Meteor.isClient) {
Meteor.subscribe('tasks');
}
I've also explained working with collections in a recent blog article.

Meteor and filepicker-plus package

I am having a little bit of trouble outputting a image after a filepicker selection with the file picker plus package from meteor. How to I grab the uploaded image url or file path, so I can pass it into a form input and put it in a collection. Putting in into the collection isnt the part I am worried about its getting the path that I am having trouble with cheers.
All contained in postSubmit template.
I have a form with
<input type="filepicker" name="myName" />
and a img output in the same template
<img src="{{filepickerIdToUrl myName}}">
and a router file containg
Router.onBeforeAction(function(){
loadFilePicker('magickey');
//can leave out key if its in settings
this.next();
},{only:['postSubmit']});
Here is the full postSubmit template
<template name="postSubmit">
<form>
<label for="title">Title</label>
<input name="title" id="title" type="text" value="" placeholder="Name your post"/>
<button id="uploadImage" class="btn btn-info btn-sm"><i class="fa fa-upload"></i> Upload</button>
<input type="submit" value="Submit"/>
</form>
<img id="imagePreview" class="img-responsive" src="{{filepickerIdToImageUrl imageId placehold_it='500x350' h=200 w=300}}"/>
<button id="removeImage" class="btn btn-warning btn-sm"><i class="fa fa-trash-o"></i> Remove</button>
This is also my postSubmit events
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
title: $(e.target).find('[name=title]').val(),
image: $(e.target).find('[name=image]').val()
};
Meteor.call('postInsert', post, function(error, result) {
// display the error to the user and abort
if (error)
return alert(error.reason);
Router.go('postPage', {_id: result._id});
});
}
});
Thanks to Nate and the google groups link above, I got it working.
Here is my solved code, Right now it only shows the preview on the form and you can remove it by clearing the session value, but it will be easy enough to grab that session value and put it into the form on submit.
Thanks again Nate.
Template.postSubmit.created = function(){
Session.setDefault("imageId", null);
Session.setDefault("imageKey", null);
};
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
title: $(e.target).find('[name=title]').val(),
image: $(e.target).find('[name=image]').val()
};
Meteor.call('postInsert', post, function(error, result) {
// display the error to the user and abort
if (error)
return alert(error.reason);
Router.go('postPage', {_id: result._id});
});
},
'click #uploadImage':function(event, template){
event.preventDefault();
filepicker.pickAndStore(
{
mimetypes: ['image/gif','image/jpeg','image/png'],
multiple: false
},{
access:"public"
},
function(InkBlobs){
// the upload is now complete to filepicker - but the form hasnt persisted the values to our collection yet
Session.set("imageId", _.last(_.first(InkBlobs).url.split("/")));
Session.set("imageKey", _.first(InkBlobs).key);
// once the session changes are made, the form will now have the new values, including a preview of the image uploaded
},
function(FPError){
log.error(FPError.toString());
}
);
},
'click #removeImage':function(event, template){
event.preventDefault();
Session.set("imageId", "remove");
Session.set("imageKey", "remove");
}
});
Template.postSubmit.helpers({
'hideRemove':function(){
return Session.equals("imageId", null) || Session.equals("imageId", "remove");
},
'imageId':function(){
if(Session.equals("imageId", "remove"))
return "";
else
return Session.get("imageId") || "";
},
'imageKey':function(){
if(Session.equals("imageKey", "remove"))
return "";
else
return Session.get("imageKey") || "";
}
});

How to combine Meteor.publish/Subscirbe and collection.allow?

I tried to write some code for understanding Meteor.publish/subscribe and collection.allow
Now It's don't have any error However I can't insert data to my collection.
testing.html
<head>
<title>testing</title>
</head>
<body>
<div class="container">
<div class="col-xs-2">
{{> loginButtons}}
</div>
<div class="col-xs-10">
{{> gettingContent}}
</div>
</div>
</body>
<template name="gettingContent">
<h1>These are all my contents.</h1>
{{#each gettingAll}}
{{title}}
{{author}}
{{/each}}
{{#if currentUser}}
<h2 class="alert">{{denie}}</h2>
<p>Title: <input type="text" id="title"/></p>
<p>Author: <input type="text" id="author"/></p>
<p><input type="button" value="Click" id="action" /></p>
{{/if}}
</template>
model.js
Content = new Meteor.Collection("Content");
Meteor.methods({
creatingContent: function(options) {
check(options, {
title: String,
author: String,
date: new Date()
});
Content.insert({
title: options.title,
author: options.author,
date: options.date
});
}
});
Content.allow({
insert: function(userId) {
if(Meteor.userId() === true){
return true;
} else {
return false;
}
}
});
clientTesting.js
Template.gettingContent.helpers({
gettingAll : function(){
return Meteor.subscribe("getAll");
}
});
var options = {
title: $("#title").val(),
author: $("#author").val(),
date: new Date()
};
Template.gettingContent.events({
'click #action' : function(event,options) {
event.preventDefault();
if(Meteor.userId() === true) {
Meteor.call("creatingContent",options);
console.log("Content was created.");
$("#title").val("");
$("#author").val("");
}else{
Session.set("deny", "You must be login for creating content.");
}
}
});
serverTesting.js
Meteor.publish('getAll', function(){
if(Content.find().count() === 0){
return Session.set("noData", "Data not found'");
} else {
return Content.find({},{sort:{'date':-1}});
}
});
I believe your problem is here: Meteor.userId() === true
By default, a Meteor userId is an alphanumeric character string. Testing for equality to true is always going to be false if the userId exists.
Try this:
if (Meteor.userId()) {
return true;
} else {
return false;
}
Or even simpler:
return !!Meteor.userId();
i didnt test this code but date: new Date() line is incorrect. It Should be Date.
check(options, {
title: String,
author: String,
date: Date
});
Okay Everyone I have fixed some code problem and just two thing I don't understand 1.How to use Meteor.subscribe with Template? and 2. How to render for showing my data. I got error like this
Exception from Deps recompute function: Error: {{#each}} currently only accepts arrays, cursors or falsey values.
testing.html
<head>
<title>testing</title>
</head>
<body>
<div class="container">
<div class="col-xs-2">
{{> loginButtons}}
</div>
<div class="col-xs-10">
{{> gettingContent}}
</div>
</div>
</body>
<template name="gettingContent">
<h1>These are all my contents.</h1>
{{#each gettingAll}}
<p>Title: {{title}}, Author: {{author}}</p>
{{/each}}
<h2 class="alert">{{denies}}</h2>
{{#if currentUser}}
<p>Title: <input type="text" id="title"/></p>
<p>Author: <input type="text" id="author"/></p>
<p><input type="button" value="Click" id="action" /></p>
{{/if}}
</template>
clientTesting
Template.gettingContent.helpers({
gettingAll : function(){
return Meteor.subscribe('getAll') && Content.find().count() ;
},
denies: function(){
return Session.get("deny");
}
});
var options1 = function(title, author){
var title = $("#title").val();
var author = $("#author").val();
if(_.isString(title) && !_.isEmpty(title) && _.isString(author) && !_.isEmpty(author) ){
var result = {title: title, author: author};
return result;
}else{
console.log("Invalid Data")
}
};
Template.gettingContent.events({
'click #action' : function(event) {
event.preventDefault();
Session.set("options", options1());
var options = Session.get("options");
if(Meteor.userId() && !_.isEmpty(options)) {
Meteor.call('creatingContent',options, function(){
console.log("Content was created.");
});
$("#title").val("");
$("#author").val("");
return Session.set("deny", "Your Data was created.");
}else {
return Session.set("deny", "Invalid Data.");
}
}
});
model.js
Content = new Meteor.Collection("Content");
Meteor.methods({
creatingContent: function(options) {
check(options, {
title: String,
author: String
});
if(options.title.length === 0)
throw new Meteor.Error(413, "noData Passed");
if(options.author.length === 0)
throw new Meteor.Error(413, "noData Passed");
Content.insert({
title: options.title,
author: options.author,
date: new Date().getTime()
});
}
});
Content.allow({
insert: function(userId) {
if(Meteor.userId() === true){
return true;
} else {
return false;
}
}
});
serverTesting.js
Meteor.publish('getAll', function(){
return Content.find({},{sort:{'date':-1}});
});

Resources