I'm trying to setup a table using alternating row colors. I'm having trouble getting Meteor to let me use the #if statement to start each row.
Here is the simple helper to determine if the row number is odd or even;
Template.drillDown.trx = function() {
return trx.find({userID: Meteor.userId()});
}
Template.drillDown.isEven = function(num) {
return !( num & 1 );
}
and I have this in the template named drillDown;
<table>
{{#each trx}}
{{#if isEven trx_num}}
<tr class="even">
{{else}}
<tr class="odd">
{{/if}}
<td>{{trx_num}}</td>
</tr>
{{/each}}
</table>
I get the error unexpected {{else}}. I've tried setting this up other ways where I pass in the entire tag like <tr class="even"> but then it throws an error when it see's the closing tag. Any suggestions on how to get past this problem?
The issue is that you are wrapping just the start tag in the {{#if}} block. In Spacebars, everything has to be a complete element with a start and end tag.
The best way to accomplish what you're looking for is:
Helper:
Template.drillDown.isEven = function() {
return !( this.trx_num & 1 );
}
HTML:
<tr class="{{#if isEven}}even{{else}}odd{{/if}}">
<td>{{trx_num}}</td>
</tr>
Also, you could avoid this whole matter entirely and use tr:nth-child(even) instead of even and odd classes: http://www.w3.org/Style/Examples/007/evenodd.en.html
I would change if helper like this:
Template.drillDown.isEven = function() {
return !( this.trx_num & 1 );
}
(I assume helper returns correct answer)
Then, if statement can look like this, cause I guess this line causes trouble
{{#if isEven}}
Also, if you have more helpers, better way to manage them is to keep them this way
Template.<template>.helpers({
isEven: function() {
return !( this.trx_num & 1 );
},
trx: function() {
return trx.find({userID: Meteor.userId()});
},
})
Related
I am trying to apply a class on an element within ng-repeat and more importantly I want to decide which class is applied based on a property of the member of the model that is currently being iterated on. To illustrate, I have a list of buildings that I'm obtaining from a REST endpoint and then rendering them in the table.
<tr ng-repeat="building in buildings">
......
<td>
<i ng-class="{'icon-check' : building.elevator, 'icon-check-empty' : building.elevator}"></i>
{{building.elevator}}
</td>
</tr>
The issue is that I'm failing to set the class on <i> element based on the value of the elevator property of the building that is being rendered.
For the record, {{building.elevator}} renders as true or false based on what came in the response.
I have also tried using a ternary operator in the class attribute with no luck.
Also tried using:
building.elevator == 'true'/'false'
instead of just:
building.elevator
with no result.
Please let me know what I'm missing here.
Thanks
EDIT
The issue was in the faulty class for Font Awesome, I have probably picked up a class that no longer exists while searching for something that looked like check/no check solution.
For reference, here is the code that works:
<i ng-class="{'fa fa-check-circle-o' : building.elevator, 'fa fa-circle-o' : !building.elevator}"></i>
and the relevant part of the server response I was getting:
[{..., "elevator":false, ...}]
Thanks Nikhilesh Shivarathri & user3273700 for your responses. You've helped me with alternatives and pointers which led me to suspect the faulty class in the end.
What do I do with my question now when the final answer wasn't even related to the code I presented here for inspection?
I've created a sample application of your requirement please check if you're missing anything in your application from below example.
Change the value.booleanField !== 'true' to value.booleanField !== true if
your data contains boolean fields not as strings.
var app = angular.module('sample', []);
app.controller('samplecontroller', function($scope) {
$scope.values = [{
booleanField: 'true',
name: 'Jospeh'
}, {
booleanField: 'false',
name: 'John'
}]
});
.test-false {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="sample">
<div ng-controller="samplecontroller">
<table>
<tr ng-repeat="value in values">
<td>{{value.name}}<td>
<td ng-class="{'test-false': value.booleanField !== 'true'}">{{value.booleanField}}</td>
</tr>
</table>
</div>
</body>
I'm starting to use meteor for a project, and there are several concepts that are being pretty hard to me coming from angular+php.
I'm trying to handle two helpers, for displaying a list of records in a table, based in a date range that I'm storing on a session variable. My template then looks like this, I'm using a helper called noRecords and another called records, records store the actual document set, and in noRecords I'm trying to store as a boolean whether the record document set is empty or not.
div(class='col-md-8 col-md-offset-2')
if noRecords
p There are no records for the selected date
else
table(class='table table-bordered')
thead
tr
td Id
td Name
tbody
each records
+record
Unfortunately I've not being able to set records and noRecords at the same time without repeating the query, in my javascript code those helpers are now defined like this:
records : function(){
var startDate = Session.get('searchDate').setHours(0,0,0,0);
var endDate = Session.get('searchDate').setHours(23,59,59,999);
var matches = Records.find(
{date : {
$gte : new Date(startDate),
$lte : new Date(endDate)
}
});
return records;
},
noRecords : function(){
var startDate = Session.get('searchDate').setHours(0,0,0,0);
var endDate = Session.get('searchDate').setHours(23,59,59,999);
var matches = Records.find(
{date : {
$gte : new Date(startDate),
$lte : new Date(endDate)
}
});
return records.count() === 0;
}
The date session variable is set by an event.
I guess there must be a better way of doing this instead of executing the query twice, I've tried using reactive variables but I had no luck, since I can't make the reactive variable to update when the minimongo query executes.
Is there any way of achieving this without running two queries?
If you are repeating the same logic in multiple helpers one solution is to reduce them to a single helper and return an object from it, e.g.:
records : function(){
var startDate = Session.get('searchDate').setHours(0,0,0,0);
var endDate = Session.get('searchDate').setHours(23,59,59,999);
var cursor = Records.find(
{date : {
$gte : new Date(startDate),
$lte : new Date(endDate)
}
});
return {
cursor: cursor,
empty: cursor.count() === 0,
one: cursor.count() === 1
}
}
And in your template:
if records.one
p Found One!
Silly example but it can be used more broadly.
Note that in your example you wouldn't actually need the noRecords helper because your template can check for an empty cursor:
each records
p date
else
p No records!
If you were using Blaze, you could use the {{#each records}}...{{else}}...{{/each}} using your records helper only like this.
<template name="myTemplate">
<div class='col-md-8 col-md-offset-2'>
{{#each records}}{{> showRecord}}
{{else}}<p>There are no records for the selected date</p>
{{/each}}
</div>
</template>
You could also use {{#if records}}...{{else}}...{{/if}} since each will iterate over each item in the records cursor
<template name="myTemplate">
<div class='col-md-8 col-md-offset-2'>
{{#if records}}
<table class='table table-bordered'>
<thead>
<tr>
<td>Id</td>
<td>Name</td>
</tr>
</thead>
<tbody>
{{#each}}{{> showRecord}}{{/each}}
</tbody>
</table>
{{else}}<p>There are no records for the selected date</p>
{{/with}}
</div>
</template>
I am working on an app I had deployed, and trying to get everything up to the latest version and update the code to take advantage of the latest processes, like subscribing in the Template.onRendered, but I have seemingly broken my sortable.
My template (simplified somewhat)
<template name="formEdit">
<div id="formContainer">
{{#if Template.subscriptionsReady}}
{{#with form this}}
<table id="headerFieldsTable" class="table">
<tbody id="headerFields">
{{#each headerFields}}
<tr class="headerFieldRow">
<td>{{> headerFieldViewRow }}</td>
</tr>
{{/each}}
</tbody>
</table>
<h5>Form Fields</h5>
<table id="formFieldsTable" class="table">
<tbody id="formFields">
{{#each formFields}}
<tr class="formFieldRow">
<td>{{> formFieldViewRow }}</td>
</tr>
{{/each}}
</tbody>
</table>
{{/with}}
{{else}}
Loading...
{{/if}}
</div>
</template>
And the template's onRendered():
Template.formEdit.onRendered(function() {
var formsSubscription = this.subscribe('formById', this.data);
var headerFieldsSubscription = this.subscribe('headerFieldsForForm', this.data);
var formFieldsSubscription = this.subscribe('formFieldsForForm', this.data);
var formEditTemplate = this;
this.autorun(function() {
if (formsSubscription.ready() && headerFieldsSubscription.ready() && formFieldsSubscription.ready()) {
formEditTemplate.$(''));
formEditTemplate.$('#headerFields').sortable({
axis: "y",
stop: function(event, ui) {
var headersToSave = [];
$('#headerFieldsTable div.headerField').each(function(idx, headerFieldDiv) {
var header = Blaze.getData(headerFieldDiv);
header.sequence = idx;
headersToSave.push(header);
});
_.each(headersToSave, function(header) { header.save(); });
}
});
formEditTemplate.$('#formFields').sortable({
axis: "y",
stop: function(event, ui) {
var feildsToSave = [];
$('#formFieldsTable div.formField').each(function(idx, formFieldDiv) {
var field = Blaze.getData(formFieldDiv);
field.sequence = idx;
feildsToSave.push(field);
});
_.each(feildsToSave, function(field) { field.save(); });
}
});
}
});
});
But for both the headers and footers, the formEditTemplate.$('#headerFields') and formEditTemplate.$('#formFields') both seem to return no results. It seems like the DOM is not actually present. I thought the .ready() call on all the subscriptions would correct that, but think there is a timing issue where Blaze hasn't fixed up the DOM yet, but the subscriptions are indeed done. I say this because I put a breakpoint in Chrome right at the first line of the if, and the browser was still showing "Loading...".
I also attempted to hot-wire things by having a helper that setup the sortable placed at the end of the {{#with}} block, hoping that maybe it would be rendered last, but that didn't work either.
I found some articles on the Meteor forums that seemed to suggest adding a timer, but this seems very "hackish". Is there a new pattern for running JS that requires the DOM to be fully initialized?
Instead of the time delay hack, I recommend you use Tracker.afterFlush() to guarantee that the DOM has been created and updated. Here is a description from Meteor docs:
Schedules a function to be called during the next flush, or later in
the current flush if one is in progress, after all invalidated
computations have been rerun. The function will be run once and not on
subsequent flushes unless afterFlush is called again.
So inside of your if statement, you can wrap the code block like so
if (formsSubscription.ready() && headerFieldsSubscription.ready() && formFieldsSubscription.ready()) {
Tracker.afterFlush( function () {
//Code block to be executed after subscriptions ready AND DOM updated
});
}
Here is a reference with examples using Tracker.afterFlush.
This question will be similar to this one I asked earlier. I got it working, copied solution, but there must be something Im missing here. I start with the code:
router.js:
this.route('note',{
path: '/note/:_id',
data: function() { return Notes.findOne(this.params._id); },
});
this.route('notes', {
waitOn: function() { return Meteor.subscribe('notes')}
});
template 'notes' :
<table id="notes-table">
{{#each notes}}
<tr id="table-row">
<td id="indicator"></td>
<td id="remove-note" class="icon-close notes-table-class"></td>
<td id="notes-title" class="Nbody notes-table-class">{{this.title}}</td>
<td id="notes-body-prev" class="Nbody notes-table-class">{{this.body}}</td>
</tr>
{{/each}}
</table>
helpers.js :
Template.notes.events({
'click .Nbody': function(events,template){
console.log('displaying note : ' + this._id);
Router.go('/note/'+this._id);
}
});
Template 'note' is simple {{title}} and {{body}}
The problem is, when I click on the note table body it does take me where it should be, which is single note, but its text just flashes for a second and disappear immediately and never comes back, so I see nothing..
Question: What is the problem?
I do not get any error in the console.
The Differences between this and 'memo' solutions are:
- here im using table instead of span's
- I dropped wrapping clickable body in 's tags ( I think this might be the reason )
You have to subscribe in 'note' route to be able to retrieve it:
client:
this.route('note',{
path: '/note/:_id',
waitOn: function() { return Meteor.subscribe('note',this.params._id )}
data: function() { return Notes.findOne(this.params._id); },
});
this.route('notes', {
waitOn: function() { return Meteor.subscribe('notes')}
});
server:
Meteor.publish('note', function(noteId){
return Notes.find(this.params._id);
})
In comment you wrote that it started to work when you : moved waitOn to Router.configure. Route.configure waitOn is working for all routes and because Method.publish('notes') function probably returns Notes.find() then note starts to work correct.
I have something like this:
<template name ="products">
<br />
<h2>Products</h2>
<table>
<tr>
<td>Name</td>
<td>Price</td>
</tr>
{{#each products.items}}
<tr>
<td>{{name}}</td>
<td>{{price}}</td>
</tr>
{{/each}}
<tr>
<td>Total:</td><td>{{products.totalPrice}}</td>
</tr>
</table>
</template>
Template.products.helpers({
products: function () {
try {
var user = Session.get("user");
return JSON.parse(localStorage[user]); //this return*
} catch (e) {
}
}
});
*this returns something like this {totalPrice: 30, items:[{"productId1","name1","10"},{"productId2","name2","20"}]}
The question is: I need to have this info stored in localStorage and not in a Meteor.Collection as i dont want to go to the server until moment X (Doesn't matter really). But I cant make this thing auto update whenever I change localStorage value. Is there any way to do this?
Thanks in advance.
That's what Dependencies are for. Simplest example:
var array = [];
var arrayDep = new Deps.Dependency();
Template.name.helper = function() {
arrayDep.depend();
return array;
};
var change = function() {
// do things to Array contents
arrayDep.changed();
};
Store the info in the Session as it is reactive so your template will change every time the value in the Session changes.
You could also use the browser-store package that seems to make localstorage reactive.