Meteor Blaze To Do App checked condition - meteor

(Super new to meteor)
I've gone through the meteor todo app tutorial and on the checking off and deleting tasks part It says that
If you try checking off some tasks after adding all of the above code, you will see that checked off tasks have a line through them. This is enabled by the following snippet:
<li class="{{#if checked}}checked{{/if}}">
With this code, if the checked property of a task is true, the checked
class is added to our list item. Using this class, we can make
checked-off tasks look different in our CSS.
Is there any way to have this apply to a different tag, and not the list? so basically have it say
With this code, if the checked property of a task is true, the updatedstyle
class is added to our img tag. Using this class, we can make
checked-off tasks make images look different in our CSS.

You can put the {{#if}} anywhere you want in your template, not just in the list item. If I understand your specific question:
<li>Some item
<img class="{{#if checked}}updatedstyle{{/if}}" src="/myImg.png">
</li>
Per #blueren, the parameter to the {{#if}} is evaluated as a truthy or falsy value. It can be a key in the current data context (i.e. this.checked) or can be returned by a helper.

If you want a class reactively binded to html tag in the template, you can do it like that:
Template code:
<template name="MyTemplate">
<img src="my/img/path.jpg" class={{checked.img}}/>
<ul>
<li class={{checked.li}}>My List Entry</li>
</ul>
</template>
Helper Code:
Template.MyTemplate.helpers({
checked() {
let liClass = '';
let imgClass = '';
//your checked condition
if(document.getElementById('myCheckBox').checked) {
let liClass = 'checked';
let imgClass = 'updatedstyle';
}
return {
li: liClass,
img: imgClass
};
}
});
That way you can bind multiple classes with different names to the checked input value.

Related

How make a dynamic value in MeteorJS

I need to set a variable or array in meteor JS which whenever changes. Wherever it is used on the page changes.
So far I have tried to set values in session.
let in = Session.get("in");
and this in HTML. The following works fine. This changes whenever it detects a change in array
{{#each in}}
<span class="selectable-tags"> {{this}} </span>
{{/each}}
But whenever I try to add these tags to other div using following. Then the ones added to other div does not change.
"click .selectable-tags"(e, t) {
// sets the tags inside the Method box on click
var el = e.target.cloneNode(true);
el.setAttribute('contenteditable', false);
document.querySelector('[contenteditable]').appendChild(el); },
using meteor with blaze UI. This can be used as reference link
Edit: Session.get or set is given for reference . This was to tell that these values are changing as they on any event triggered wherever set.
You need to add a helper that returns the session variable and show the helper in your html code :
Template.yourtemplate.helpers({
inHelper: ()=> {
return Session.get('in');
}
)}
And in html :
<div>{{inHelper}}</div>
Every modification on in by Session.set('in', newValue) will change your HTML.

vue.js reference div id on v-on:click

Using v-on:click I'd like to set a variable with the id of the div in Vue.JS - how do I reference this?
<div id="foo" v-on:click="select">...</div>
<script>
new Vue({
el: '#app',
data: {
},
methods: {
select: function(){
divID = this.id // ??
alert(divID)
}
}
})
</script>
You can extend your event handler with the event object $event. That should fit your needs:
<div id="foo" v-on:click="select($event)">...</div>
The event is passed on in javascript:
export default {
methods: {
select: function(event) {
targetId = event.currentTarget.id;
console.log(targetId); // returns 'foo'
}
}
}
As mentioned in the comments, `$event` is not strictly necessary, when using it as the only parameter. It's a nice reminder that this property is passed on, when writing it explicitly.
However, nobody will stop you from writing the short notation:
<div id="foo" #click="select">...</div>
Beware that the method will not receive the `$event` object when you add another parameter. You need to explicitly add it at the position you will handle it in the listener. Any parameter order will work:
<div id="foo" #click="select(bar, $event)">...</div>
To find more options of the v-on directive, you can look through the corresponding entry in the vue documentation:
Vue API Documentation - v-on
Inspired by #nirazul's answer, to retrieve data attributes:
HTML:
<ul>
<li
v-for="style in styles"
:key="style.id"
#click="doFilter"
data-filter-section="data_1"
:data-filter-size="style.size"
>
{{style.name}}
</li>
</ul>
JS:
export default {
methods: {
doFilter(e) {
let curTarget = e.currentTarget;
let curTargetData = curTarget.dataset;
if (curTargetData) {
console.log("Section: " + curTargetData.filterSection);
console.log("Size: " + curTargetData.filterSize);
}
}
}
}
Just to highlight another option than the selected answer for the same question, I have a delete button on a record and want to perform an action with the record's unique id (a number). I could do the selected answer as before:
<button :id="record.id" #click="del">×</button>
This leaves the unfortunate reality that my del function needs to pull the id attribute out of the javascript event, which is more about the API (the DOM) than my domain (my app). Also using a number as an element id isn't ideal and could cause a conflict if I do it more than once in a view. So here's something that's just as clear and avoids any future confusion:
<button #click="()=>del(record.id)">×</button>
methods: {
del(id) {
fetch(`/api/item/${id}`, {method:"DELETE"})
}
}
You see, now my del function takes the record id instead of an event, simplifying things.
Note that if you do this wrong, you will invoke your delete function immediately, which is not what you want. Don't do this:~~
<button #click="del(record.id)">×</button>
If you end up doing that, Vue will call the del function every time this html fragment is rendered. Using the anonymous function ()=>del(record.id) will return a function that's ready to be executed when the click event happens.
Actually #nirazul proved this is fine. Not sure what my issue was.

Re-rendering of a template doesn't allow me to permanently change an element's class?

I have a sortable list.
<template name="the_playlist">
{{#each main_list}}
<li id="{{index}}" class="list_element">
<div class="next_song">...</div>
<div class="destroy">...</div>
<div class="element_style">{{song_title}}</div>
</li>
{{/each}}
</template>
And this is the main_list that it prints from.
Template.the_playlist.main_list = function(){
//if ret is valid, it will have a songs member
var ret = Links.find().fetch()[0];
if (typeof ret == 'undefined'){
ret = []
}
else {
ret = Links.find().fetch()[0].songs;
}
return ret;
}
And I am using the sortable plugin and more importantly its update callback which updates everytime the user changes a position the list or an element is added to the list.
$(function() {
$( "#playlist" ).sortable({
update: function(){
Template.list.updateList(); //MODIFIES DB CONTENTS, AND MAIN_LIST's VALUES CHANGE
}});
$( "#playlist" ).disableSelection();
});
*The problem: * If a page already has list elements when it's loaded, for one time only, I would like to add a class that hides (.addClass("hide")) each of the next_song elements that are on the page at that time. This *will work only until main_list changes* by a call to Template.list.updateList above, after which automagically, the added class will disappear - most likely due to the re-rendering that is occuring since the main_list depends on the db changes.
The following in the JQuery snippet I use to try and accomplish this.
$("#playlist li .next_song").each(function(){
$(this).addClass("hide_song");
})
Here is a demo. Try plugging in the above JQUery code into the console. and then move the list elements around to see the problem.
Can you not just determine whether that will be the case in a helper function?
Template.the_playlist.helpers({
'list_elements_exist': function() {
return (!!$('#playlist li').length);
}
}
Then you can just insert the logic straight into the template:
<div class="next_song{{#if list_elements_exist}} hide_song{{/if}}">...</div>
To be honest, I'm not 100% sure that this will float with reactivity depending on the structure of your app. If it doesn't work properly, I'd introduce a new session boolean, list_elements, the value of which is returned by the helper function above. It should be fairly easy to update its value in event handlers or created callbacks to keep it tracking whether there are any items in the list or not, and this will guarantee the list renders as required regardless of other dependencies changing.

Template is re-rendered even though there is no data change

I'm struggling with an issue that I will explain giving a simple demo.
There's following very simple document in People Collection.
{
"_id" : "vxmLRndHgNocZouJg",
"fname" : "John" ,
"nicks" : [ "Johnny" , "Jo"]
}
Now let's consider following templates. Basically I display username and a list of nicknames with input field for adding more nicknames.
<head>
<title>test</title>
</head>
<body>
{{> name}}<br/>
{{> nicks}}
</body>
<template name="name">
<input type="text" value="{{fname}}"/>
</template>
<template name="nicks">
{{#each nicks}}
<div>{{this}}</div>
{{else}}
no nicks yet
{{/each}}
<input type="text" name="nicks"/>
<input type="submit"/>
</template>
My client javascript code is as follows:
Template.name.fname = function() {
return People.findOne({"fname" : "John"},{
transform : function(doc) {
return doc.fname;
}
});
}
Template.name.rendered = function() {
console.log('Template "name" rendered!');
}
Template.nicks.nicks = function() {
var john = People.findOne({"fname" : "John"});
if(john) return john.nicks;
}
Template.nicks.events({
'click input[type="submit"]' : function () {
var johnId = People.findOne({"fname" : "John"})._id; // demo code
People.update(johnId,{
$addToSet : {
nicks : $('input[name="nicks"]').val()
}
})
}
});
My problem is that after adding nickname (update of nicks field in a document) template name is re-rendered (I know because I console.log it). When I query People collection to provide data for name template I use transform option so changes in nicks field shouldn't have impact on name template.
Meteor docs supports this:
Cursors are a reactive data source. The first time you retrieve a cursor's documents with fetch, map, or forEach inside a reactive computation (eg, a template or autorun), Meteor will register a dependency on the underlying data. Any change to the collection that changes the documents in a cursor will trigger a recomputation.
Why template name is re-rendered then?
The template is re-rendered because you change the People collection.
When you alter the People collection, Meteor automatically assumes that everything that it provides data to needs to be recalculated. (Which your name template does via Template.name.fname.
Even though you transform the output of the cursor, the People collection has changed in some way. The query is done before the transform is used, in other words, its not the transform that is looked at but the whole collection.
Meteor thinks that perhaps your document with {'fname':'John'} may have some other field that might have changed and it needs to requery it to check (which the nicks field has been altered). The transform is then applied after the requery.
Your HTML might not actually change at this point, only if the cursor returns something different will the html be changed.
If it becomes an issue in any scenario (i.e forms being cleared or DOM being changed when it shouldn't be) you can use the {{#isolate}} {{/isolate}} blocks to ensure that only everything inside them is re-rendered and nothing outside.

Meteor - Select Parent ID of element clicked

Using Meteor, I am trying to loop through and display a list of notes from a database with an option to delete each note.
Here is the HTML (using Handlebars.js)
<template name="Notes">
{{#each NoteArr}}
<div class="Note">
<h2>{{Title}}</h2>
<p>{{Body}}</p>
<span class="deleteNote">Delete</span>
</div>
{{/each}}
</template>
And here is the client Javascript
Template.Notes.events = {
"click .deleteNote" : function(){
noteID = $('.deleteNote').parent().attr("id");
Notes.remove({ID:noteID});
}
};
This grabs the first instance of .deleteNote, so unless I'm trying to delete the first one, that won't help. How can I grab the parent of the particular instance of .deleteNote that was clicked, not just the first one it finds?
The reason why the first element is deleted is.. in your .click event, you are accesssing the div directly as $('.deleteNote').parent() which grabs the first node in the html which has a class .deleteNode.
Now to remove the specific notes, from the collection: Every document in the collection has a unique _id attribute which is generated automatically. assign that unique _id of the document to the span element as <span id= "{{_id}}" class="deleteNote">Delete</span>.
So the cilck event will look like:
Template.Notes.events = {
"click .deleteNote" : function(e){
var noteID = e.currentTarget.id;
Notes.remove({_id:noteID});
}
};
And the template will look like:
<template name="Notes">
{{#each NoteArr}}
<div class="Note">
<h2>{{Title}}</h2>
<p>{{Body}}</p>
<span id= "{{_id}}" class="deleteNote">Delete</span>
</div>
{{/each}}
</template>
Untested code, but hope this will help solving your issue.
The _id of a note is stored in 'this' as well. In addition, the remove function accepts '_id' as a string. So this should work as well:
Template.Notes.events = {
'click .deleteNote': function(){ return Notes.remove(this._id)}
}
A few benefits here. Less querying the DOM for information. Less jQuery. Fewer lines of code to think about. Cleaner templates.

Resources