Accessing Parent Data in a Meteor Template Helper - mongodb

I have a template for a collection of Groups. Each group document contains an array with each element containing a number and a boolean. The template is laid out as follows:
<template name="group">
<li class="{{#if checked}}checked{{/if}} groupBox">
<button class="delete">×</button>
<input type="checkbox" checked="{{checked}}" class="toggle-checked-group" />
<span class="text">{{name}}</span>
<ul>
{{#each this.numbers}}
<li class="checked">
<button class="deleteNumber">×</button>
<input type="checkbox" checked="{{checked}}" class="toggle-checked-number" />
<span class="text">{{number}}</span>
</li>
{{/each}}
</ul>
<form class="new-number">
<input type="text" name="number" placeholder="Type to add a number to this group" />
</form>
</li>
</template>
An example group document is laid out as such:
{
"_id": "pSpNcJKDPhRtGhtov",
"name": "Sample Group",
"createdAt": "2015-06-27T00:45:39.137Z",
"owner": "t2ELweZsZqJXNQqdZ",
"username": "brodan",
"checked": false,
"numbers": [
{
"number": "1234567",
"checked": true
}
]
}
I need to be able to remove a specific number from the numbers array when I click on the deleteNumber button. I have the following javascript and can't figure out how to get the parent context to use in the MongoDB query:
Template.group.events({
"click .deleteNumber": function () {
Meteor.call("deleteNumber");
}
});
Meteor.methods({
deleteNumber: function (numbers) {
//Need to delete number from group via MongoDB query here.
}
});
I know that I can use the $pull operator in my MongoDB query in the deleteNumber method, but in order to get the right group, I need to have the parent data from the number in the template.
Solved thanks to xamfoo's answer.
The working deleteNumber method is now set up like this:
deleteNumber: function (groupId, number) {
Groups.update(groupId, {$pull: {numbers: {"number": number}}});
}

You should be able to get the context via this in the event handler and group data with Template.instance().data:
Template.group.events({
"click .deleteNumber": function () {
var data = Template.instance().data;
Meteor.call("deleteNumber", data._id, this.number);
}
});
In this case, parameters of deleteNumber are:
Meteor.methods({
deleteNumber: function (groupId, number) {
//Need to delete number from group via MongoDB query here.
}
});
You can also access the data context with Template.currentData() or Template.parentData().

Related

How to change the state of a vue when you click the check button

I am creating a Todo Application using Vue.js, Express.js, and MongoDB.
I want to change the state of the fields that appear as v-for through the checkbox.
The code I want is to change the state of the text when the checkbox is checked and to show another element with v-if.
Here is the code I wrote: But it does not work.
If I have the wrong code, please help me.
List Component
<template>
<div class="todos">
<div v-for="todo in todos" v-bind:key="todo._id" class="todo">
<div>
<input type="checkbox" v-model="todo.done" v-on:click="completeTodo(todo)">
<del v-if="todo.done">{{todo.title}}</del>
<strong v-else>{{todo.title}}</strong>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
todos: {}
}
},
created () {
this.$http.get('/api/todos')
.then((response) => {
this.todos= response.data
})
},
methods: {
completeTodo (todo) {
todo.done = !todo.done
}
}
}
</script>
You don't need v-on:click="completeTodo(todo)". v-model is already doing the trick for you. Also, the todos in your code should be defined and instantiated as array not an object.
v-model is used for two way data binding. That means, whatever data you pass from your code will be bound to the checkbox value in this case and whenever a change is made from UI and value of checkbox is altered by user, that will be captured in v-model variable. v-model is a combo of :value prop and #change event in case of checkbox, hence, it is able to update data in both the ways.
Please refer this snippet.
var app = new Vue({
el: '#app',
data: {
todos: [
{
id: 1,
title: 'Study',
done: false
},
{
id: 2,
title: 'Work',
done: false
},
{
id: 3,
title: 'Play',
done: false
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="todos">
<div v-for="todo in todos" :key="todo.id">
<div>
<input type="checkbox" v-model="todo.done"/>
<del v-if="todo.done">{{todo.title}}</del>
<strong v-else>{{todo.title}}</strong>
</div>
</div>
</div>
</div>

Accessing nested objects in a Meteor application using Blaze and Spacebars

I am doing a Meteor project for educational purpose. It's a blog with a post list page and a post detail page where logged-in users can add comments. Disclaimer: In the project I cannot use aldeed-simple schema and I won't use pub/sub methods. I am using iron-router and the accounts-password package for user authentication.
Step 1
I' ve set a basic application layout and a basic routing:
Router.route('/posts', function () {
this.render('navbar', {
to:"navbar"
});
this.render('post_list', {
to:"main"
});
});
Router.route('/post/:_id', function () {
this.render('navbar', {
to:"navbar"
});
this.render('post_detail', {
to:"main",
data:function(){
return Posts.findOne({_id:this.params._id})
}
});
Step 2
I ve created a post detail view which include a comment form. Find the code below:
<template name="post_detail">
<div class="container">
<p>Title: {{title}}</p>
<h5>Description: </h5>
<p>{{description}}</p>
<h4>Comments</h4>
<ul>
{{#each comment in comments}}
<li>{{comment}}</li>
{{/each}}
</ul>
{{>comments}}
</div>
</template>
<!-- Comments -->
<template name="comments">
<form class="js-add-comment-form">
<input type="text" id="comment" class="form-control"/>
<button type="submit" class="btn btn-default js-add-comment">Add a comment</button>
</form>
Step 3
I've added an event helper to the comment template in order to add user comments. The form takes the comment and the user Id. See code below:
Template.comments.events({
'submit .js-add-comment-form': function(event) {
event.preventDefault();
var comment = event.target.comment.value;
console.log(comment);
var user = Meteor.userId();
var email = Meteor.user().emails[0].address;
console.log(email)
console.log(user);
if (Meteor.user()){
Posts.update(
{_id: this._id},
{$push: {comments: {comments:comment, user}}});
event.target.comment.value = "";
}
}
});
Find below a post detail view in the browser:
I've inserted some comments but I cannot display it on the page. Instead I get and Object object for each comment and user id. If I go to the console this is my object:
How can I access those objects and display those field values(comments and user) on the page? Any help ? Thanks Sandro.
This is happening because {{comment}} is an object itself. Here's some sample code to get the message property showing:
<template name="post_detail">
<div class="container">
<p>Title: {{title}}</p>
<h5>Description: </h5>
<p>{{description}}</p>
<h4>Comments</h4>
<ul>
{{#each comment in comments}}
<li>{{comment.comments}} | {{emailForUser comment.user}}</li>
{{/each}}
</ul>
{{>comments}}
</div>
</template>
If you want to get the user's email address as per my example above, you'll want to add a template helper:
Template.post_detail.helpers({
emailForUser( id ) {
var user = Meteor.users.findOne({_id: id});
if( user ) {
return user.emails[0].address;
}
}
});

Dynamic scope in Autoform pushArray (attach reply into specified commentId)

I have singlePost with postId. In each singlePost, I list comment.
Autoform for comment:
{{#autoForm id="updateCommentArray" type="update-pushArray" collection=Collections.Posts doc=commentDoc scope="comment" template="semanticUI"}}
{{> afQuickField name="content"}}
<div class="form-group">
<button type="submit" class="ui positive button">Submit</button>
<button type="reset" class="ui negative button">Reset</button>
</div>
{{/autoForm}}
What Autoform provide is to use scope to attach new array into specified field. For example, when I use scope comment.0.reply, that reply will attach to first array of comment. When I use scope comment.1.reply, that reply will attach to second array of comment. Etc
How to make it dynamic? What I thought is to use commentId, but how?
Thank you
I think it works like this:
The scope defines to which array inside a document the form is adding.
The doc is the document where the form shall add data to the scope.
For example (i did not test this, but it is similar to code i used):
javascript:
CommentsSchema = new SimpleSchema({
comment:{
type:String,
autoform:{
rows: 2
}
}
});
PostSchema = new SimpleSchema ({
content:{
type:String,
autoform: {
rows: 2
}
},
comments:{
type:[CommentsSchema]
}
});
template:
{{#each posts}}
{{#autoForm id=this._id type="update-pushArray" collection='Posts' doc=this scope="comment" template="semanticUI"}}
{{> afQuickField name="content"}}
<div class="form-group">
<button type="submit" class="ui positive button">Submit</button>
<button type="reset" class="ui negative button">Reset</button>
</div>
{{/autoForm}}
{{/each}}

Unable to get mizzao/meteor-autocomplete to work with collection

I am using mizzao/meteor-autocomplete and am having problems in trying to get it to work.
When viewing the page in my browser, I am getting no results at all when typing any text. I've created the appropriate collection:
Institutions = new Mongo.Collection("institutions");
and know that there is data in the actual db, however still no success.
I've included my files below.
publications.js (located in the server folder)
Meteor.publish('institutions', function(args) {
return Institutions.find({}, args);
});
registrationStart.js
I've two helpers; one that actually powers the search and the other that should be returning the institutions. I have also tried this with the token: '#' argument with no success.
if (Meteor.isClient) {
Template.registrationStart.helpers({
settings: function() {
return {
position: "top",
limit: 7,
rules: [{
collection: Institutions,
field: "name",
options: '',
matchAll: true,
template: Template.institutionSelectDisplay
}]
};
},
institutions: function() {
return Instititions.find();
}
});
Template.registrationStart.events({
"autocompleteselect input": function(event, template, doc) {
// Session.set(event.target.name, event.target.value);
console.log("selected: ", doc);
console.log("event.target.name: ", event.target.name);
console.log("event.target.value: ", event.target.value);
}
});
}
registrationStart.html template
<template name="registrationStart">
<div class="panel-body" id="loginForm">
<h2 class="pageTitle">veClient Registration</h2>
<form>
<div> </div>
<fieldset>
{{> inputAutocomplete settings=settings id="institution" class="input-xlarge" placeholder="type institution here"}}
</fieldset>
<div> </div>
<button type="submit" class="btn btn-primary btn-sm">Continue Registration</button>
</form>
</div>
</template>
And the template to be rendered
<template name="institutionSelectDisplay">
<p class="inst-state">{{city}}, {{state}}</p>
<p class="inst-name">{{name}}</p>
<p class="inst-description">{{email}}</p>
</template>
Problem resulted because there was no subscription to the "institutions" publication. So need to add a subscribe statement to the registrationStart.js file:
Meteor.subscribe('institutions');

Get the list from 2 relational tables and display

I have tables "Category & SubCategory". CategoryID is "foreign key" in SubCategory Table. I need to fetch all the Category and related SubCategories and bind in an Accordion list.
This is my code:
<div id="accordian" data-bind="foreach:Categories">
<div class="panel-heading">
<h4 class="panel-title"><span data-bind="text: CategoryName"></span></h4>
</div>
<div class="panel-body">
<ul data-bind="foreach:SubCategories">
<li><span data-bind="text: SubCategoryName" ></span></li>
</ul>
</div>
</div>
Please help me on this.
I have created a demo with fake response which should be provide via you endpoint. There were also missing self declaration in order to have object reference. So simply add following in begging of you VM.
var self = this;
I have also created fake method which simulate fake data array the same as you service should to provide.
function ajaxHelperFakeResponse(callback) {
var response = [{
CategoryName: 'catgeory1',
SubCategories: [{
SubCategoryName: 'SubCategoryName1'
}, {
SubCategoryName: 'SubCategoryName2'
}]
}];
callback(response);
}
You can see whole solution in my fiddler http://jsfiddle.net/jakethashi/ow785abr/