Rendering elements of an array from a document in Meteor - mongodb

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>

Related

Nested lightning aura iteration

I have a nested aura:iteration like this:
<aura:iteration items="{!v.results}" var="res">
<tr class="slds-hint-parent">
<aura:iteration items="{!v.ColumnsNameArr}" var="colName">
<td>
<div class="slds-truncate" >
{!res[colName]}}
</div>
</td>
</aura:iteration>
</tr>
</aura:iteration>
How can I write correct {!res[colName]}? It is possible to do something like this in lightning?
It looks like you want to iterate the res property, what looks like to me of type Map. In Lightning Aura it's not possible to iterate over a Map (Apex) / Object (Javascript).
What you can do as solution would be to transform the res Map to a type of Array (Javascript) or in Apex to type of a List.
In Lightning Aura it is possible to iterate Array's.
So how would that look like:
{
process: function() {
var res = { a: 1, b: 2};
var resArray = [];
for (var key in res) {
resArray.push({
key: key,
value: res[key]
});
}
// now, from here on you can use the 'resArray' to iterate in your Aura component
}
}
It is possible. Use the iteration variable at the next level down.
I think this will work...
<aura:iteration items="{!v.results}" var="res">
<tr class="slds-hint-parent">
<aura:iteration items="{!res.ColumnsNameArr}" var="colName">
<td>
<div class="slds-truncate" >
{!colName}}
</div>
</td>
</aura:iteration>
</tr>

Call a Method From Inside dom-repeat in Polymer

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.

Dynamic add element to the top of the page

PROBLEM: I have table with tr that have data from table, When I insert element in Mongo and post it on my page, element added on the bottom of the table
Posts = new Mongo.Collection('posts');
Meteor.publish('allPosts', function(){
return Posts.find({}, { sort: { date: -1 }} );
});
Meteor.subscribe("allPosts");
Posts.insert({
date: 1
})
Posts.insert({
date: 2
})
Posts.insert({
date: 3
})
<table class=" table">
{{#each posts}}
{{> postJobs}}
{{/each}}
</table>
<template name="postJobs">
<tr class="smallInfo">
<td>{{date}}</td>
</tr>
</template>
In DOM I have:
<table>
<tr>
<td>1</td> // must be 3
</tr>
<tr>
<td>2</td> // must be 2
</tr>
<tr>
<td>3</td> // must be 1
</tr>
</table>
All my last inserts must add to the top of my page (table)
EDIT :
My problem with dynamic insert in collection (and i know about -1 gramatik mistake)
EXAMPLE :
Meteor.startup(function(){
Meteor.setTimeout(function(){Test("10");}, 1000);
function Test(x)
{
Posts.insert( {
submitDate: moment().format()
});
}
Meteor.setTimeout(function(){Test("10");}, 10000);
If i sort by submitDate it will show like :
<tr><td>10-10-10</td></tr> // must be 10-10-20
<tr><td>10-10-20</td></tr> // must be 10-10-10
BUT when i refresh my page(F5) all ok
<tr><td>10-10-20</td></tr>
<tr><td>10-10-10</td></tr>
Why do you not sort descending?
{sort: { date: -1 } }
Cheers,
Tom
UPDATE:
You can find a live example at a MeteorPad I prepared:
http://meteorpad.com/pad/Ba5DTe94NjFi3ZTPA/Playground_Flow-Router_Chat
You have to do the sort on the client side not within the publish method.
This is why you get first time sorted but then just as inserted returns.
If you do the sort on client side find() the minimongo will do it on each new document.
Hope this helps for you.
Tom

AngularJS rows are displayed with empty data

I am testing out angularJS.
In app.js I have
function ListCtrl($scope, Restangular) {
Restangular.all("employee").getList().then(function(employee) {
$scope.employee = employee;
console.log($scope.employee.emp);
});
}
and in html I have
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Emp No</th>
<th>Name</th>
<th><i class="icon-plus-sign"></i></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="employee | filter:search | orderBy:'ename'">
<td>{{employee.empno}}
</td>
<td>{{employee.ename}}</td>
<td>
<i class="icon-pencil"></i>
</td>
</tr>
</tbody>
</table>
Problem I am facing is there are empty rows being displayed with no data being displayed.
What could be the reason for this?
Edit 1
JSON returned from server
{"emp":[{"empno":"7369","ename":"SMITH","hiredate":
"1980-12-17T00:00:00+03:00","job":"CLERK","mgr":"7902","sal":"800"},
{"comm":"300","empno":"7499","ename":"ALLEN","hiredate":
"1981-02-20T00:00:00+03:00","job":"SALESMAN","mgr":"7698","sal":"1600"},
{"comm":"500","empno":"7521","ename":"WARD","hiredate":
"1981-02-22T00:00:00+03:00","job":"SALESMAN","mgr":"7698","sal":"1250"},
{"empno":"7566","ename":"JONES","hiredate":
"1981-04-02T00:00:00+03:00","job":"MANAGER","mgr":"7839","sal":"2975"},
{"comm":"1400","empno":"7654","ename":"MARTIN","hiredate":
"1981-09-28T00:00:00+03:00","job":"SALESMAN","mgr":"7698","sal":"1250"},
{"empno":"7698","ename":"BLAKE","hiredate":
"1981-05-01T00:00:00+03:00","job":"MANAGER","mgr":"7839","sal":"2850"},
{"empno":"7782","ename":"CLARK","hiredate":
"1981-06-09T00:00:00+03:00","job":"MANAGER","mgr":"7839","sal":"2450"},
{"empno":"7788","ename":"SCOTT","hiredate":
"1987-04-19T00:00:00+03:00","job":"ANALYST","mgr":"7566","sal":"3000"},
{"empno":"7839","ename":"KING","hiredate":
"1981-11-17T00:00:00+03:00","job":"PRESIDENT","sal":"5000"},
{"comm":"0","empno":"7844","ename":"TURNER","hiredate":
"1981-09-08T00:00:00+03:00","job":"SALESMAN","mgr":"7698","sal":"1500"}]}
console log from chrome browser
[Object, Object, Object, Object, Object, Object, Object, Object,
Object, Object, Object, Object, Object, Object, route: "employee",
getRestangularUrl: function, addRestangularMethod: function, one:
function, all: function…]
0: Object
empno: "7369"
ename: "SMITH"
hiredate: "1980-12-17T00:00:00+03:00"
job: "CLERK"
mgr: "7902"
sal: "800"
Based on the JSON you've included it looks like $scope.employee should contain a one key called "emp", which is what you print to the console. You might need to change your ng-repeat to work with that.
Also, I'm unfamiliar with the form of your ng-repeat expression. I believe they are supposed to follow a form similar to "something in somethings" so in this case instead of just employee you may want that to be employee in employee.emp.
In a more general sense, the Angular Batarang plugin for Chrome is infinitely helpful for solving these sorts of problems.

How to focus a DOM child element from a Meteor event handler

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));
}
});