How to keep your model in sync with an {{#each}} block? - meteor

I have something like this:
Template.todoList.helpers({
todos: function() {
return Todos.find({}); // Returns records with a todoText, ownerId and done field.
}
});
And then in the template I use a {{#each}} block to list the todos. But I want to be able to change if they are done with a checkbox. If I just add a checkbox like this in the {{#each}} block, it will correctly display the initial state, but if I toggle the checkbox, the record will not update. I would need to keep track of the _id of the record, but where would I store it? If I can get hold of the correct _id the rest is very simple:
Template.todoList.events({
'change .doneCheckbox': function(event) {
var todoId = ??;
Todos.update(todoId, {$set: {done:event.target.checked}});
}
});
What would I insert at the place of ???

Should be available from this (more specifically this._id):
var todo = this,
todoID = todo._id;
You can access other properties from the record too (e.g. this.done).

Related

Selected option tag using Meteor

I have an Items collection with a boxId field (and a Boxes collection), and I want to be able to, through a select tag, change an item's boxId.
Here's my template:
And this is how I define the boxOptions helper:
How can I get an item's boxId and use it to find the proper option tag, and then give it the selected attribute?
Create an event
Template.item.helpers({
"change select": function(event){
const boxId = event.target.value;
items.update({_id: this._id}, {$set: {boxId: boxId}});
}
})
Note this assumes you are using the packages insecure and autopublish. If you don't use these and you really should not, then you best read about:
Parameter validation
Publications
Meteor Methods
Use Template.parentData() to have access to the item's id. Here's the helper:
selected: function () {
if (this._id == Template.parentData().boxId) {
return "selected";
}
}

Know when child templates have been rendered

<template name="FrameItems">
<div class="frame-items">
{{#each frames}}
{{> FrameItem}}
{{/each}}
</div>
</template>
In the above example, I want to know when all FrameItem templates inside FrameItems template have been rendered. I thought onRendered of the parent would be invoked when all the child templates have been rendered, but it was just called right away. What's the conventional way of making sure all the child templates are rendered?
One way to do it is to use a counter and increment it until it reaches a certain value.
Here the counter would in Session and incremented until it reaches the length of your Frames iterable thing:
Template.FrameItems.onRendered(function() {
Session.set('frameCounter', 0);
});
Template.FrameItem.onRendered(function() {
Session.set('frameCounter', Session.get('frameCounter') + 1);
});
Then you simply use a tracker:
//Where template is your template instance, for example 'this' in an onCreated callback
template.autorun(function doStuffWhenFramesRendered(computation) {
if(Session.get('frameCounter') === template.frames.length) {
doStuff();
//Stop observing
computation.stop();
}
});
Note that it takes into account the fact that FrameItem may render at weird times (avoiding race conditions if any), but it doesn't take into account new frames. To take those into account you would not stop the computation.
Here is how I would proceed:
You create a pageSession reactive variable or reactive dictionary entry. Let's call it lastRendered.
You update it in the onRendered function of your FrameItem template using the _id of the related frames item. This way, each time a FrameItem template is rendered, you now which one it is.
You create an helper in your parent template watching your lastRendered reactive variable and checking if it matches your last frames item. It could look like that (untested code):
lastFrameIsRendered: function() {
var lastId = frames.find().limit(1).sort({$natural:-1}).fetch()._id;
return pageSession.get ("lastRendered") === lastId;
},
Alternatively, if you need to get a feedback in your parent template onRendered function, you can wrap this code into a this.autorun(function() { (tracker) like this:
var lastId = frames.find().limit(1).sort({$natural:-1}).fetch()._id;
this.autorun(function() {
if (pageSession.get ("lastRendered") === lastId) {
//do your stuff
}
});
It will be executed each time there is a change in your parent template.

meteor,Dynamically change template helper data

I have a small doubt,Is it possible to change the template helper data dynamically.
Here is my template
{{#each post}}
<h4>{{postyname}}</h4>
<h4>{{Headline}}</h4>
<h4>{{Location}}</h4>
{{/each}}
and this is my helper data
post:function()
{
posts.find({},{sort: {createdAt: -1}}).fetch();
}
and it is displaying results in my homepage and i have search bar at the top of this page and whenever user clicks search button the same template must be render but with different data depend on user search.
I've tried like this on my onclick event,looks like it is not working
'onclick #searchBtn':function()
{
var all_posts=posts.find({'Headline': search},{sort: {createdAt: -1}}).fetch();
Template.postDisplay.post=function(){
return all_posts;
}
How to do this?
I believe using Dep Dependency is what you want to use:
Create a variable on the top of your js file
var _deps = new Tracker.Dependency;
var searchCriteria = {};
In your helper:
post:function() {
_deps.depend(); // creates a dependency between the accessor of "post" and the _deps
posts.find(searchCriteria,{sort: {createdAt: -1}}).fetch();
}
On your button click:
'onclick #searchBtn':function() {
searchCriteria = {'Headline': search};
_deps.changed(); // notifying everyone that is dependent on _deps that it has changes
}
Based on what I see from your question I believe this should handle your situation. Let me know if I misunderstood something in your problem.
Why not just use a Session variable? They are reactive, just what you need.
Instead of manually handling a dependency, using Session.set and Session.get will register and fire dependencies automatically..
post:function()
{
var searchCriteria = Session.get("searchCriteria") || {};
posts.find(searchCriteria,{sort: {createdAt: -1}}).fetch();
}
'onclick #searchBtn':function()
{
Session.set("searchCriteria", {'Headline': search});
}
The Session variable would also be preserved after a hot code reload. A variable would not.

Strange UI behavior for {{#each}} on an array

Meteor newbie here. Working off of the Todos example, I am trying to use a separate Tags collection. It seems to be working except for a strange UI artifact: If I click a tag in the tag filter, and check off the last item in the todo list, the first item gets checked as well. The first item does not get updated to done, and clicking away from the tag filter and then back shows the first item unchecked as it should be. So I am not sure why that is happening.
The code for the todos is the same as in the Todos example
{{#each todos}}
{{> todo_item}}
{{/each}}
And the code for the tags collection filter
var todos = [];
if (!currentTaskId)
return {};
var tag_filter = Session.get('tag_filter');
if (tag_filter){
var tags = Tags.find({taskId: currentTaskId, name: tag_filter});
tags.forEach(function(tag){
var todo = Todos.findOne(tag.todoId);
todos.push(todo);
});
return todos; // this is an array rather than a collection and causes a strange artifact bug when checking bottom todo as done
}
What I have been able to gather is that if you do {{#each}} on an array you create a dependency on the entire scope rather than each individual item in the array, versus a collection cursor that automagically creates a dependency for each document in the collection. Has anybody run into this odd UI behavior? I'd also like to know if there is a way to either make the array into a cursor or at least act like one by registering a dependency for each item in the array?
Appreciate any insights, thank you.
I've revamped your code to return a cursor instead of an array, it may solve your problem but it's untested.
var tagFilter=Session.get("tag_filter");
if(!currentTaskId || !tagFilter){
return null;
}
// find tags and fetch them in an array
var tags=Tags.find({
taskId:currentTaskId,
name:tagFilter
}).fetch();
// build an array of Todos ids by extracting the todoId property from tags
// see underscore docs
var todosIds=_.pluck(tags,"todoId");
// return todos whose id is contained in the array
return Todos.find({
_id:{
$in:todosIds
}
});

Append the updated values of the database to textarea using meteor

I am new to meteor and learning to create web application looking at examples. I want to append the updated values of the database to html textarea.
For example if i update value for key "sensor", the sensor value needs to be appended to textarea. How can I do that using meteor?
You use a handlebars helper and bind changes to your text area to update the field: e.g
html
<template name="hello">
<textarea name="address">{{address}}</textarea>
</template
client side js
//Your collection that stores your data
MyCollection = new Meteor.Collection("mycollection");
//Your handlebars helper to give some data to address
Template.hello.address = function() {
var address = MyCollection.findOne({name:address});
if(address) return address.value;
}
//Something to bind changes to your address to your collection
Template.hello.events({
'change [name=address]' : function(e,cxt) {
var address = MyCollection.findOne({name:address});
MyCollection.update(address._id, {$set:{value:e.currentTarget.value}});
}
});
Finally you need something on your server side js too:
//The same collection as on your client
MyCollection = new Meteor.Collection("mycollection");
//Insert an address on the first time if there is nothing in yet:
Meteor.startup(function() {
if(!MyCollection.findOne({name:address})) {
MyCollection.insert({name:address,value:"10 Downing St, London, United Kingdom"});
}
});
Thats the basic gist of it to update a text area when its changed, and to show a value in your template . When its updated it will also reflect the update across to all tabs/everyone viewing the page.

Resources