Waiting for template render in Meteor, inconsistent - templates

Been scratching my head on this, so here's the simplest way to view it - I'm using this:
Template.today.rendered = function() {
console.log(this.firstNode.children.length);
};
Simply to try and get the count of items that are supposedly rendered. The template looks like:
<template name="today">
<div class="todaySlider">
{{#each dayOfWeek}}
{{> singleDay}}
{{/each}}
</div>
</template>
and if it's of any importance, singleDay looks like:
<template name="singleDay">
<div class="day {{isCurrent}}">
<h2 class="date">{{date}}</h2>
{{#each items}}
{{> item }}
{{/each}}
</div>
</template>
I'm trying to wait for all the "singleDays" to render, however that count I'm logging is usually different on refresh. I'll get anything from 0 to the correct value, and I don't understand why. This seems to be the right place to call it, I fear that maybe the double "each" is too slow?
I've tried timers (which I honestly shouldn't) and even DOM Mutation Observers (which seem like overkill) but surely there is a pure Meteor approach to this, any ideas?

Template.rendered happens when the template is rendered, but that doesn't mean there'll be any data in it.
I'm pretty sure you'll need to do this inside a helper.
each helpers don't have to return cursors, they can also return an array. If the number of "singleDays" is short, you could send an array to the template instead of a cursor. It's kind of ugly, and there might be a better way to do this, but I think this will work.
Template.today.helpers({
dayOfWeek: function() {
var days = DaysCollection.find({}).fetch();
if (days[days.length - 1]) days[days.length - 1].isLast = true;
return days;
}
});
I assume {{isCurrent}} is where you add the extra class that you're talking about. If so, just have the isCurrent helper look for this.isLast to be true.

Seems like what is happening here is that the template is being rendered before the Mongo collection is sent to the client. To be more specific, meteor renders your template as fast as possible, which means that it has no concept of 'waiting' for any data to be sent from the server to the client. Therefore, if you place anything indirectly regarding database queries inside of a non-reactive call (Template.rendered), then it will execute with the data as undefined.
I'm assuming your dayOfWeek helper looks something like this:
Template.today.helpers({
daysOfWeek: function () {
var today = CollectionName.findOne();
return today.daysOfWeek;
}
})
(Or maybe you are using the router to pass the day directly to the template)
Either way, within your router you need to wait for the Mongo collection item to be sent to the client before any rendering takes place. If you are using Iron-Router, you simply have to 'wait' for your data/subscription.
More information can be found here: https://github.com/iron-meteor/iron-router/blob/devel/Guide.md#wait-and-ready
If you are still using autopublish, then you can replace the subscription with your database query.

Related

Ember helper has undefined values

I can't figure out why the helper I created in app/helpers/shop-name.js:
import { helper } from '#ember/component/helper';
export function shopName(shop) {
return shop.identifier + '-' + shop.name;
}
export default helper(shopName);
doe not format the values as needed in my template application.hbs:
<select>
{{#each model as |shop|}}
<option value={{shop.id}}>{{shop-name shop}}</option>
{{/each}}
</select>
I get undefined-undefined displayed in my select list.
If I do the same conversion directly in the template:
<option value={{shop.id}}>{{shop.identifier}}-{{shop.name}}</option>
it works as needed. What's wrong with that helper ?
I'm using Ember 3.0.
Thank you.
I'm not sure that helpers can take inputs that aren't strings. I hope someone can confirm.
Anyway, your use case is really better handled at the model level, using a computed property:
shopName: computed('identifier', 'name', function() {
return `${identifier} - ${name}`;
})
and then you would just use {{shop.shopName}} in your template
There are multiple issues in your code.
shop is an object and I bet it's an Ember.Object. If dealing with Ember.Objects, you should always use get() method to retrieve values for now. This might change soon. Since you are not using get() method, you get undefined. Embers template engine is using getter and therefore it's a huge difference between shop.identifier in your helper and {{shop.identifier}} in your template. Resolve the issue by shop.get('identifier') or import { get } from '#ember/object'; [...] get(shop, 'identifier'); Use the last one if you can't be sure that shop is an instance of Ember.Object.
Since you are passing shop and not shop.identifier as argument to your helper, the helper will only be recomputed if the reference of shop changes (e.g. replaced by another object). Changes to the value of it's properties will not recompute the helper.
One solution for you problem is to use a computed property as suggested by ctcpip. Another one is the concat helper: {{concat shop.identifier '-' shop.name}}.

Pass current route to template

I've been playing around with golang for a couple of weeks now, and recently started to look into the revel framework.
In other languages/frameworks that I've used, it's always been possible to check what the current route is from within the template. Something that made it easier for me to keep things like navigation in a separate template file, and just do this:
<!-- in header template -->
<ul class="nav">
{{ if eq .req.action "App.Index" }}
<li class="active"><a href="#">
{{ else }}
<li><a href="{{url "App.Index" }}">
{{end}}
Home</a></li>
<!-- other links here -->
</ul>
This isn't the actual code I'm writing, but I hope this makes it clear what the idea is: Have a template for the navigation handy, and set classes/links according to which action is the current one.
After some time going through the source code, and a number of golang template examples, I couldn't quite see any way in which the current action or anything else is exposed to the template.
To get around this, I'm currently using a func interceptor which automatically sets a render argument for me:
func prependRoute(c *revel.Controller) revel.Result {
c.RenderArgs["_current_route"] = c.Action
return nil
}
This is working fine, but it feels a bit hacky. I think I'm probably missing something really obvious that would allow me to reserve func interceptors for when I really need them.
The question, then, is simple: What is the correct, and most reliable way to find out what the current action is in a revel template?

How can I determine if a component returns nothing dynamically?

I have a ember component that is accessing a database and returning the results in a datatable type UI component. I would like to be able to use "N/A" when the result of the component is null or nothing.
For example, I have:
{{each bar in foobars}}
<td class="classyTD">
{{getBars bar=bar}}
</td>
{{/each}}
This works great when I have data, but returns nothing when I don't have data. The designers would prefer an "N/A". Modifying the database isn't an option and while modifying the component getBars is an option, it will be extremely painful.
Is there a method/way to handle this after the execution of the component? If not, or if it's a terrible idea - I'll suffer through changing the component, I trust the community's opinion.
You really should do this inside the component template. You can give the N/A string as a parameter, if that's of any help: http://emberjs.jsbin.com/lemabekuwi/2/edit?html,css,js,output
Or you could change the component that it indicates emptiness through a class and use some css magic: http://emberjs.jsbin.com/duqazahegi/1/edit?html,css,js,output
If you want to limit logic in the handlebars you can have the following in the component js file:
({
setBar: (function() {
if (!this.get('bar')) {
return this.set('bar', 'N/A');
}
}).observes('bar')
});

How to render a component based on model parameter

I have the requirement to render a template based on a property of my model.
My stratergy was to use custom if block helpers for the handle bars templates, something like this:
{{#ifequals type 'cars'}}
{{cars-component}}
{{/ifequals}}
{{#ifequals type 'planes'}}
{{planes-component}}
{{/ifequals}
but I cant create a block helper that resolves parameter passed by the template to the helper.
if I use Handlebars.registerHandlebars, it resolves the variable name insted of the variable.
the reason i need to do this is because it is part of a plugin framework.
Option blocks (special if statements) don't work properly in ember handlebars helpers. https://github.com/emberjs/ember.js/issues/2237
isCar: Ember.computed.equals('type', 'cars')
isPlane: Ember.computed.equals('type', 'planes')
{{#if isCar}}
{{cars-component}}
{{/if}}
{{#if isPlan}}
{{planes-component}}
{{/if}}
It almost looks like you can do it, but there is a problem with it, as is pointed out in the github issue above.
Ember.Handlebars.helper('iff', function(value,property, options) {
if(value === property){
return options.fn.apply();
} else {
return options.inverse.apply();
}
});
http://emberjs.jsbin.com/UhOWeWiJ/1/edit
http://emberjs.jsbin.com/UhOWeWiJ/2/edit
You will get a bound blocks error when you try and implement something like this and the values actually change
https://github.com/emberjs/ember.js/issues/2237
Uncaught You can't use appendChild outside of the rendering process
http://emberjs.jsbin.com/UhOWeWiJ/3/edit
http://emberjs.jsbin.com/UhOWeWiJ/3/edit

Ember/Handlebars: compare variable with string

I need to display some block depending on the current views variable in comparison to some string:
PSEUDO code is:
{{#if view.someVariable "desiredValue"}}
Hurray desiredValue exists in this view!
{{else}}
Sorry, doesn't match...
{{/if}}
Of course it doesn't work as if statement allows only one param. I tried with custom registerHelper of Handlebars, but it doesn't resolve the view.someVariable, instead uses it as a string (although view.someVariable is defined and has value).
Finally I also tried with Handlebar's registerBoundHelper - it resolves the params BUT doesn't support Handlebar's blocks, what's more tries to resolve the string as an object, so fail again. Pure madness!
My views are very dynamical they depends on many factors also from backend and approach from the pseudo code would be perfect for resolving it. Is there any way to do that?
Edit:
There is a sample of what I want ... http://jsfiddle.net/biesior/dMS2d/ looks quite trivial...
[if] statement inside the each is pseudo code of course
You can just declare a computed property, something like isDesired on your view, which would compare someVariable to your string:
App.MyView = Ember.View.extend({
// ...stuff...
isDesired: Ember.computed.equal('someVariable', 'desiredValue')
});
And have a simple conditional in the template:
{{#if view.isDesired}}
Hurray desiredValue exists in this view!
{{else}}
Sorry, doesn't match...
{{/if}}