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}}.
Related
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.
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')
});
Suppose I have a computed property on a view:
TestView=Ember.View.extend({
computedProperty:function(){
return this.get("elementId")+"test"
}.property("targetObject.{{elementId goes here}}")
})
I want to make the property depend on the model property with the attribute name equivalent to the id on that view element. However, I haven't found an elegant way to do this. I need to do this to make my code as general as possible. My solution so far is to make the property dependent on targetObject's id and then call object.notifyPropertyChange(id) whenever I need binding to update.
To add some clarity. Suppose I have a view that displays a number that is bound to a property on the model. However, I want the number to be formatted with commas when it is displayed. I could easily write a computed property for each possible attribute that will do the formatting. Instead though, I just want a generic view that will know to take the value of the attribute of the model with the same name as the view's elementId property. Here is an example of my hacky solution:
TestView=Ember.View.extend({
computedProperty:function(){
var id=this.get("elementId")
return addCommas(this.get("targetObject").get(id))
}.property("id")
})
I then call:
object.notifyPropertyChange(id) whenever one of the attributes on the model changes. It works and achieves my goals but I was looking for a more elegant solution.
In the view the formatted value is displayed in an custom textfield.
{{view App.CustomTextField value=computedProperty id="someAttribute}}
Just use a helper which formats numbers with commas. You don't need any properties, computed or otherwise.
test.hbs
Hello, I am a formatted {{add-commas number}}.
helpers/add-commas.js
Ember.Handlebars.helper('add-commas', function(value) {
return value.toLocaleString();
});
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
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}}