I'm having this situation where I need to call a method from the dom-repeat. Below is my code
<template is='dom-repeat' items="[[_dataArray]]" as="rowItem">
<template is='dom-repeat' items="[[_objectArray]]" as="columnItem">
<template>
<span>[[_getColumnItemValue(rowItem, columnItem)]]</span>
</template>
</template>
</template>
and in my _getColumnItemValue method, I want to get the value for an object with key specified by the columnData attribute.
Like rowData[columnData]
_getColumnItemValue: function(rowData, columnData) {
return rowData[columnData];
}
My problem is the method _getColumnItemValue is not being called. Is there any better way to do achieve this?
If your code is exactly as you pasted, then you have one too many <template> tags.
<template is='dom-repeat'>
<template is='dom-repeat'>
<span></span>
</template>
</template>
The innermost template must be removed. You are rendering that instead of the <span>.
Finally i was able to make this thing working. Not exactly in this case, but in another project, with exact same logic. The only change was my _objectArray was not an array of strings, it was an array of objects. So the code will look like this:
<template is="dom-repeat" items="{{tableData}}" as="rowData">
<tr>
<template is="dom-repeat" items="{{headers}}" as="columnData">
<td>
<template is="dom-if" if="{{checkType(columnData, 'check')}}">
<paper-checkbox disabled="{{getAttributeValue(columnData, 'disabled')}}" checked="{{getRowData(rowData, columnData)}}" on-change="checkBoxSelected"></paper-checkbox>
</template>
<template is="dom-if" if="{{checkType(columnData, 'led')}}">
<led-indicator on="{{!getRowData(rowData, columnData)}}"></led-indicator>
<span style="display: none;">{{getRowData(rowData, columnData)}}</span>
</template>
<template is="dom-if" if="{{checkType(columnData, 'link')}}">
{{getRowData(rowData, columnData)}}
</template>
<template is="dom-if" if="{{checkType(columnData, 'text')}}">
<span>{{getRowData(rowData, columnData)}}</span>
</template>
</td>
</template>
</tr>
</template>
See the method getRowData
getRowData: function (row, column) {
return row[column.key];
},
and
checkType: function (columnData, type) {
var isType = false;
isType = columnData.type.toLowerCase() == type.toLowerCase();
return isType;
},
This is for a table, which can dynamically add or remove rows and columns and show different type of elements like, a text, link, checkbox some of my custom controls like led-indicator etc.
The logic behind is, the headers array will be used to generate the table columns, this array contains objects of structure
{
name: 'Popular Name',
key: 'PopularName',
type: 'text'
}
and table data contains array of object, which contains the key specified in the headers array.
Hope this may be useful for some one.
Related
Example:
<template name="list_customers">
<p><h3>List Customers</h3></p>
{{#each customers}}
{{> list_customers_content}}
{{/each}}
</template>
<template name="list_customers_content">
..display all customer data from "Customers.find({})...
{{> list_customers_content_ip}}
</template>
<template name="list_customers_content_ip">
.. display data from Customers_ip.find({}) based on #each customer object id.
</template>
Template.list_customers.helpers({
customers() {
return Customers.find({});
},
});
How can this be done in fewest possible queries to two different collections, and is there any way to use the customers context object id? Please write full example.
What should the helper for Customers_ip.find() look like?
You need to pass the current document (which is this inside the #each context) to the list_customers_content template in order to further process it.
The name of the parameter will be the name to access the value in the child template.
Example:
{
name: "John Doe",
_id: "1232131",
}
<template name="list_customers">
<p><h3>List Customers</h3></p>
{{#each customers}}
{{> list_customers_content customer=this}}
{{/each}}
</template>
<template name="list_customers_content">
<span>Name: {{customer.name}}</span><!-- John Doe -->
{{> list_customers_content_ip customerId=customer._id}}
</template>
<template name="list_customers_content_ip">
{{#each customerIp customerId}}
<span>IP: {{this.ip}}</span>
{{/each}}
</template>
A helper for customerIp could look like this:
Template.list_customers_content_ip.helpers({
customerIp(customerId) {
// assumes, that these documents have
// a field named "customerId"
return Customers_ip.find({ customerId });
},
});
I'm extremely new to Meteor and haven't been able to find the answer to something that's probably laughably simple.
I have a Meteor collection called Ingredients:
Ingredients = new Mongo.Collections("ingredients");
if (Meteor.isClient) {
Template.body.helpers({
ingredients: function() {
return Ingredients.find({});
});
}
Which is populated with documents like the following:
{ name: Boneless Pork Chop,
tags: [Paleo, Pork, Local] }
Right now I'm rendering the name in a template, as follows:
<template name="ingredient">
<tr>
<td>{{name}}</td>
</tr>
</template>
What I need to figure out now is how to also render the individual elements of the 'tags' array in that template. Preferably, I'd like to render them in such a way that later I could assign a click event to each of them so they could be individually removed or edited... which from my earlier reading means I might want to put the tags in their own collection and join them to the Ingredients documents by an ID, which is perfectly find with me if that's a better pattern.
Little help? Thanks!
you can use #each to print an array in meteor.
<template name="ingredient">
<tr>
<td>{{name}}</td>
<td>
{{#each tags}}
{{this}}
{{/each}}
</td>
</tr>
</template>
I have an event handler that sets a session variable to change the content within a DOM element -- in this case a table cell.
'dblclick td.itemName': function (evt) {
Session.set("editItemName",true);
evt.currentTarget.children[0].focus();
},
<td class="itemName">
{{#unless editItemName}}
{{name}}
{{else}}
<input class="editItemName" type="text" value="{{name}}" style="width:100px;">
{{/unless}}
</td>
Pretty straight forward...
However evt.currentTarget.children doesnt work. Once the input takes place of the text, I'd like to make it automatically focus... The meteor docs say that this is a DOM object so its weird that the children function doesnt work...
Thanks
Chet
When you double click, and your function runs, you set the session editItemName to true, and then you're trying to give the input-element focus, but the template has not been re-rendered yet, so the input-element hasn't been created (the template will be re-rendered some time after your function returns). In other words: evt.currentTarget.children[0] is not a reference to the input-element.
Possible solution 1
In HTML 5 there's an attribute called autofocus, which you can use (at least I can in Chrome). Just add it to the input-element:
<input autofocus="autofocus" class="editItemName" type="text" value="{{name}}" style="width:100px;">
Possible solution 2
Otherwise you have to focus it with JavaScript when the template been rendered and your input-element exists in it:
Template.yourTemplate.rendered = function(){
var input = this.find('.editItemName')
if(input){
input.focus()
}
}
You are trying to set the focus to a DOM element that has not been rendered yet.
The issue has been bothering me for a while. I have tried to use the autofocus='autofocus' HTML attribute: it has no effect in Firefox, and in Chrome, it seems to only work the first time the element is rendered.
So we need a handler that is called just after the template is rendered, in order to set the focus with javascript. Template.templateName.rendered looks like the way to go, but there is an issue:
What didn't work for me:
<template name="itemName">
<td class="itemName">
{{#unless editItemName}}
{{name}}
{{else}}
<input type="text" value="{{name}}">
{{/unless}}
</td>
</template>
Template.itemName.rendered = function()
{
this.$('input').focus()
}
When doing this, Template.yourTemplate.rendered seems to fire only the first time you click on the item (you get the focus correctly only once).
What worked for me:
<template name="itemName">
<td class="itemName">
{{#unless editItemName}}
{{name}}
{{else}}
{{> itemNameEdit}}
{{/unless}}
</td>
</template>
<template name="itemNameEdit">
<input type="text" value="{{name}}">
</template>
Template.itemNameEdit.rendered = function()
{
this.$('input').focus()
}
Any explanation from a Meteor expert?
As #Chet pointed out, Template.[name].rendered no longer fires whenever a template is updated, but instead, only when the template is first rendered, and only once.
One can pass a callback to Tracker.afterFlush which will fire every time the template is updated.
i.e. all reactive updates are processed
Template.myTemplate.events({
'dblclick td.itemName': function(e, t) {
Session.set("editItemName",true);
Tracker.afterFlush(function() {
this.find('input').focus();
}.bind(t));
}
});
I´m working on a meteor example. I get the value of one tag on click event on the link. That value is the same that is present on one collection inside doc "pet" or "zoo". I want to use this value to filter the content present on the template.
A minimal example:
{{#each Animal}}
<div>
<span> {{pet}} </span>
</div>
<div>
<span> {{zoo}} </span>
</div>
{{/each}}
After click:
{{#each Animal}}
<div>
<span> {{zoo}} </span>
</div>
{{/each}}
On this case when I get the value present in "zoo" I just want to update the template with all the the spans that contains elements on doc zoo, and that all related to pet dissappear.
The query to mongodb is working perfectly, my problem is that I´m a little bit confused.
Should I use helpers?
Thank you so much.
Let's see if I understood correctly your problem.
You should use a Session variable where you store the action you are doing. Then add a template if and print inside of this tag whatever you want to show at that time.
Let's do a minimal example:
<template name="showAnimalsTemplate">
{{if showAnimals}}
{{#each Animal}}
<div>
<span> {{pet}} </span>
</div>
<div>
<span> {{zoo}} </span>
</div>
{{/each}}
{{/if}}
{{if showZoo}}
{{#each Animal}}
<div>
<span> {{zoo}} </span>
</div>
{{/each}}
{{/if}}
Following this example, you add in the client javascript something like this:
Template.showAnimalsTemplate.showAnimals = function(){
if( Session.get('action') == 'showingTheZoo')
return true;
return false;
}
Template.showAnimalsTemplate.showZoo = function(){
if( Session.get('action') == 'showingTheZoo')
return true;
return false;
}
Don't forget to set the session value inside the click event.
Session.set('action', 'showingTheZoo');
I have the following
<div id="header">
{{> header}}
</div>
<div class="hidden content_box">
{{> content}}
</div>
"content_box" is hidden at the start.
template "header" has a single button.
<template name="header">
<button id="display_content">Click to display content</button>
</template>
template "content" is just a simple div
<template name="content">
It's me, content
</template>
When I click on the button in the header, I want to "show" the content_box.
How do I achieve this? - or better yet, what is the best way to achieve this type of effect where you need to access the DOM of a template from an event of another template?
Template.header.events "click button#display_content": (e) ->
Template.content.show() ?????
I don't know if it is the best way to do it, but in similar situations what I've done before is to use a session parameter to store the show/hide status of the div. In your click event, you then only need to change the value of the session flag. In the template of the div you want to show/hide, you just return the class name.
Example in JS:
Template.header.events({
"click button#display_content": function () {
Session.set('contentShow', true);
}
});
Template.content.className = function (input) {
return Session.equals('contentShow', true) ? '' : 'hidden';
};
Html
<template name="content">
<div class="{{className}} content_box">
It's me, content
</div>
</template>
You'd need to initialise the session flag to false in Meteor.startup() for example Session.set('contentShow', false);. As session is reactive, the div class name will be re-evaluated automatically when you change the session flag.