Meteor templates. How can I not render a HTML element based on an {{#if}}? - mongodb

I have a collection that stores phone numbers for companies.
If a company has a phone number, draw those phone numbers.
If a company has no phone number, don't draw any.
Currently it half works. It will not draw the phone numbers if no numbers are in the collection, but it still draws the < h4 >Phone< /h4 > heading and I don't want it to.
Here's the code:
<template name="orgPage">
<h2>Organisation Name: {{name}}</h2>
<h3>Contact Details</h3>
<ul>
{{#if phone}}
<h4>Phone</h4>
{{#each phone}}
<li>{{number}} ({{type}})</li>
{{/each}}
{{else}}
<p>No contact numbers</p>
{{/if}}
</ul>
</template>
and
Template.orgPage.helpers({
'phone': function() {
return organisationsPhoneNumbers.find({ orgId: currentOrgId })
}
});
How can I get it to NOT draw the < h4 >Phone< /h4 > if there is no data returned from the collection?

short answer
Keep all of your original code and replace {{#if phone}} with {{#if phone.count}}
long answer
Spacebars has a really cool path evaluation feature, which is best explained with an example.
Imagine you have a post document in your current context. Each post is modeled to have a fetchAuthor helper, which returns a user document. Let's suppose you need the lower cased version of the author's last name. In JavaScript you could write something like:
post.fetchAuthor().profile.firstName.toLowerCase()
Now if we need that value in a template we can write:
{{post.fetchAuthor.profile.firstName.toLowerCase}}
As spacebars evaluates each identifier in the path, it checks to see if it's a function - if it is, it invokes it. Note this only works if the called functions take no arguments.
Circling back to our original example, the phone helper returns a cursor, which has a count function. We can write {{#if phone.count}} and spacebars will figure out that we mean phone.count() because count is a function.

I faced this problem early on, here's a simple approach where you return an object from the helper that includes the count:
js:
Template.orgPage.helpers({
'phone': function() {
var cursor = organisationsPhoneNumbers.find({ orgId: currentOrgId });
return { count: cursor.count(), items: cursor };
}
})
html:
{{#if phone.count}}
<h4>Phone</h4>
{{#each phone.items}}
<li>{{number}} ({{type}})</li>
{{/each}}
{{/if}}

There is a fairly standard pattern for this kind of scenarios that avoids re-running the same helper multiple times:
<template name="orgPage">
<h2>Organisation Name: {{name}}</h2>
<h3>Contact Details</h3>
{{#with phone}}
{{#if count}}
<h4>Phone</h4>
<ul>
{{#each .}}
<li>{{number}} ({{type}})</li>
{{/each}}
</ul>
{{else}}
<p>No contact numbers</p>
{{/if}}
{{/with}}
</template>
The with block sets the scope for its content to the result of thephone helper, which is a cursor.
It then checks if the count() helper/method is truth-y. If so, it uses an each iterator tor render the list of items, else - the message indicating no numbers is displayed.
Note that there is an each...else clause that works if you don't need anything outside the each block.

Related

How to use filter-collections with meteor

I'm trying to use the package doctorpangloss:filter-collections in my project.
What I have now is the following:
Server:
Meteor.publish('locations', function() {
return Locations.find();
});
Router:
this.route('foci', {
path: '/foci',
(...)
},
waitOn : function () {
return Meteor.subscribe('locations');
}
});
Client:
----.html file----
<template name="foci">
<div class="row">
{{> LocationList}}
</div>
</template>
<template name="LocationList">
{{#each locationData}}
(...)
{{/each}}
</template>
----.js file----
Template.LocationList.helpers({
locationData: function(){
return Locations.find()
}
});
Everything standard till now. On the /foci page the data is displayed.
But I would like to have a list (or actually not a list, but tiles, but thats irrelevant here) that I can filter. I've tried my best with this package, but nothing worked. Based on the documentation ( https://github.com/workpop/filter-collections/ ) it should be a few minutes work to get it done, yet I've spent way more on trying to figure it out.
If anyone could help adding the standard filter bar to the page, or knows of another similar package I could use it would be appreciated.

How to reach _id of document when it's in the parent data context?

This seems like an open-and-shut case for Template.parentData(), but to this day I've never once managed to get that bad boy working properly.
What I want is an event that updates a document depending on which button was clicked, but the buttons are themselves dependent on an array buried deeper in the document, where the _id doesn't exist.
Here's what I have:
First, a helper that sets the context peopleList:
Template.people.helpers({
peopleList: function() {
return People.find()
}
Which I use to iterate through in the HTML, printing out the first and last name of each person stored in the database, as well as their favorite colors (extraneous markup removed):
{{#each peopleList}}
<li>
{{firstName}} {{lastName}}
{{#each favoriteColors}} <button>{{this}}</button> {{/each}}
</li>
{{/each}}
It should be noted at this point that favoriteColors is a key inside the document which holds an array. So the whole thing looks something like this:
{
firstName: "Johnny",
lastName: "Boy",
favoriteColors: ["red", "blue", "blanchedAlmond"]
}
Imagine now that I want to be able to press any of these buttons, which hold the favorite colors, to set the, uh, super-duper favorite color or something. So a button click on blanchedAlmond should update the document, adding the key masterColor with the value blanchedAlmond.
The event:
'click button': function() {
var masterColor = ????
var docId = ????
Meteor.call('setMasterColor', masterColor, docId)
}
I could provide HTML data-tags that hold the color value (because this inside the event spits out some weird array with each letter separated for some reason) and even the _id with {{../_id}}, but that feels like cheating, and I really want to learn how to do the same thing inside a helper or an event.
I strongly feel like this would be a case for Template.parentData() but it returns nothing at all when I console.dir it. What should I do?
The confusion around parentData has to do with the event context. The event is attached to the template whose context is something that isn't a person or a color. Whenever you get the feeling that you need to start littering your code with data- attributes, the answer is nearly always to add more templates. For example:
html
<template name="myTemplate">
<ul>
{{#each peopleList}}
{{> person}}
{{/each}}
</ul>
</template>
<template name="person">
<li>
{{firstName}} {{lastName}}
{{#each favoriteColors}}
{{> color}}
{{/each}}
</li>
</template>
<template name="color">
<button>{{this}}</button>
</template>
js
Template.color.events({
'click button': function() {
// this context is a color - remember to convert it to a string
var masterColor = String(this);
// the parent context is a person
var docId = Template.parentData(1)._id;
return Meteor.call('setMasterColor', masterColor, docId);
}
});

About how to use array to make a meteor tag app

After read the discovermeteror, I want to make a tags app.
It will be like how Stack Overflow's questions are tagged.
So, my code is:
Tags.insert ({ food : ['apple','hotdog','meat','bean']});
food = function () { return Tags.find() }
your food is<span class="tag"> {{food}}</span>
However, all tags will appear in one tag.
What should I do to divide them?
each accepts arrays too not only cursors
{{#each food}}
<span class="tag"> {{this}}</span>
{{/each}}
food = function () {
return Tags.findOne({yourselector}).food;
}
or
{{#each alltags}}
{{#each food}}
<span class="tag"> {{this}}</span>
{{/each}}
{{/each}}
alltags = function () {
return Tags.find();
}
Here is the working Meteorpad, you can see how they are sorted on a <ul> element, just to show how they are not siameses twins

Couldn't manipulate Images.find() in CollectionFS for MeteorJS app

My app is sort of like TelescopeJS, but a lot simpler. I'm trying to echo the particular image that has been added in the post-adding form which takes an input of the name of the post, picture, categories and description. It has 2 collections, one for Articles and the other for Images (NOT a mongo collection, it's an FS collection.) The articles collection stores the name,description and category name and the other one stores image. **My Problem is: ** in the FS collection doc, the loop
{{#each images}}
<img src="{{this.url}}" alt="" class="thumbnail" />
{{/each}}
Where images: returns Images.find({}) and my articles code is :
{{#each articles}}
<li style="margin-right: 1%;">{{>article}}</li>
{{/each}}
Where articles: returns Articles.find({})
MY articles template HAS the images loop and this causes ALL THE IMAGES in the collection to be shown in one post. I just want specific images to be shown for the specific post.
These are the events:
'change .img': function(event, template) {
FS.Utility.eachFile(event, function(file) {
Images.insert(file, function (err, fileObj) {
//Inserted new doc with ID fileObj._id, and kicked off the data upload using HTTP
});
});
},
'click .save':function(evt,tmpl){
var description = tmpl.find('.description').value;
var name = tmpl.find('.name').value;
var date=new Date();
var cat = tmpl.find('.selectCat').value;
Articles.insert({
description:description,
name:name,
time:date.toLocaleDateString()+' at '+date.toLocaleTimeString(),
author:Meteor.userId(),
userEmail:Meteor.user().username,
category:cat,
});
}
<template name="article">
{{#each images}}
<img src="{{this.url}}" alt="" class="thumbnail" />
{{/each}}
Here goes the {{name_of_post}}
Here {{the_category}}
Here {{the_description}}
</template>
So what happens is, all the images that I've uploaded so far shows in one post and all the posts' picture looks the same. Help please!
You should know that fsFile support Metadata so maybe you don't need the Articles Collection
So we can make a new eventHandler.
'click .save':function(evt,tmpl){
var description = tmpl.find('.description').value,
file = $('#uploadImagePost').get(0).files[0], //here we store the current file on the <input type="file">
name = tmpl.find('.name').value,
date=new Date(),
cat = tmpl.find('.selectCat').value,
fsFile = new FS.File(file); // we create an FS.File instance based on our file
fsFile.metadata = { //this is how we add Metadata aka Text to our files
description:description,
name:name,
time:date.toLocaleDateString()+' at '+date.toLocaleTimeString(),
author:Meteor.userId(),
userEmail:Meteor.user().username,
category:cat,
}
Images.insert(fsFile,function(err,result){
if(!err){
console.log(result) // here you should see the new fsFile instance
}
});
}
This is how our new event will look, now our .save button insert everything on the same collection.
This is how we can access to the FS.File instances fields using the keyword 'metadata.fieldName'.
For example.
Teamplate.name.helpers({
showCategory:function(){
// var category = Session.get('currentCategory') you can pass whatever data
// you want here from a select on the html or whatever.
//lets say our var its equal to 'Music'
return Images.find({'metadata.category':category});
}
})
Now we use that helper on the html like any normal collection
<template name="example">
{{#each showCategory}}
Hi my category is {{metadata.category}} <!-- we access the metadata fields like any normal field on other collection just remember to use the 'metadata'keyword -->
This is my image <img src="{{this.url}}" >
{{/each}}
</template>

iMacros - How do I TAG URL with unique surrounding html?

I need to extract "https://www.somesite.com/Some.Name.123" from the code below.
That code segment is repeated many times, and I need the URLs ..Some.Name.X.
There are other code segments between each of the ones I'm interested in, with very different surrounding html. I don't need the ..Some.Name.x URLs in those other segments.
The following is unique to what URLs I need: "<a class="-cx-PRIVATE-uiImageBlock__image"
<div class="clearfix pvm">
<a class="-cx-PRIVATE-uiImageBlock__image -cx-PRIVATE-uiImageBlock__largeImage lfloat"
aria-hidden="true" tabindex="-1" href="https://www.somesite.com/Some.Name.123">
I don't know how to tag that preceding HTML with iMacros, or how to do that with jQuery as the structure will a bit different each time, but you could to this.
Save the web pages with iMacros. Write a program (c, etc.) to read each of the saved files and write the URLs that follow "cx-PRIVATE-uiImageBlock__image" to a file. Add that list of URLs to an iMacro, or have iMacros read the file, and then process each URL from iMacros.
You need to use some scripting.
My answer makes use of jQuery
var listoflinks = []; //array containing your links
$('a[href*="somesite.com"]').each(function () { // for each link that contains somesite.com in href
var j = $(this).attr('href'); //put the whole href in a variable
listoflinks.push(j); // put all values in an array
});
you'll end up with an array that contains all the href values you're looking for.
If you want to see an example and/or you want to play around with the script you can go here:
http://jsfiddle.net/flish/rESjg/
Edited
Your code is still not clear enough, but hopefully this may help
<a class="sibling a" href="link">sibling a</a><br />
<div class="sibling div"><br />
<a class="child a" href="start-with-link/correct-link">Child a</a><br />
</div><br />
Above is the markup I've used. What this means is that I have considered that you have the following elements:
a // with a sibking div
div // with a child a
a // and all of them have appropriate classes
For this markup you can use the following code (jQuery, of course)
var listoflinks = []; //array containing your links
$('a[class="sibling a"]').siblings('div[class="sibling div"]').children('a[class="child a"]').each(function () {
if ((($(this).attr("href")).substring(0,15))=="start-with-link"){
var i = $(this).attr("href");
listoflinks.push(i);
}
});
View detailed example at http://jsfiddle.net/flish/HMXDk/
Be that as it may, you can add more sibling and children elements in case you have other html entities you forgot to mention
<a class="-cx-PRIVATE-uiImageBlock__image" ------------------ <div class="clearfix pvm"> <a class="-cx-PRIVATE-uiImageBlock__image -cx-PRIVATE-uiImageBlock__largeImage lfloat" aria-hidden="true" tabindex="-1" href="somesite.com/some.name.123">
For example, what means ------------------ in your code above?