Handlebar helper syntax in Ember CLI - ember.js

In this post
Iterating over basic “for” loop using Handlebars.js
An example of a 'repeat' helper is layed out.
helper
Handlebars.registerHelper('times', function(n, block) {
var accum = '';
for(var i = 0; i < n; ++i)
accum += block.fn(i);
return accum;
});
template
{{#times 10}}
<span>{{this}}</span>
{{/times}}
I can't seem to write this out the 'CLI' way... can someone enlighten me?
First of all, it will be it's own helper file in /helpers , and it should have a dash so the resolver recognizes it. - so I wouldn't be registering it explicitly.
Default generated helper looks like this helpers/repeat-times.js (template should be the same...)
import Ember from 'ember';
export function repeatTimes(input) {
return input;
}
export default Ember.Handlebars.makeBoundHelper(repeatTimes);
so, no need to register, no need to set the name... I just can't find clear docs on the syntax. :/ (I took 20 or so stabs at it...)
Or should I be making a component instead? as suggested here: Block helper with ember-cli

#Kalman is right that you can't register a bound helper with block notation, so in this case I would recommend a component, which was referenced in the comment.
However, for those that might have found this question and still want to create a handlebars helper in ember-cli, you'll want to use the makeBoundHelper function.
For example, here's a current-date helper that I use:
// app/helpers/current-date.js
import Ember from 'ember';
export default Ember.Handlebars.makeBoundHelper(function() {
return moment().format('LL'); // Using moments format 'LL'
});
Then, in your handlebars template, you can use this:
{{current-date}}
Which yields a date like March 5, 2015

Related

How to use tabulator with ember?

I want to use http://tabulator.info/ with ember. I don't understand the documentation nor can I find any guides on configuration for ember. How can I start by creating a simple table?
Looking at the examples on the site, http://tabulator.info/, it seems that tabulator only needs an element to work (and some config).
So, our end goal is going to be to use a modifier with the ability to pass the tabulator config to it.
So, this is what we'll end up with:
<div {{tabulator someConfig}}></div>
Now, unfortunately, it looks like tabulator only accepts an id in its constructor. so we'll need to dynamically add that in to appears tabulator.
First thing you'll want to do is install https://github.com/ember-modifier/ember-modifier (be sure to read the docs as this is fun stuff)
Then, create a file in your app, app/modifiers/tabulator.js
and use these contents:
import Modifier from 'ember-modifier';
import Tabulator from 'tabulator';
import { guidFor } from '#ember/object/internals';
export default class TabulatorModifier extends Modifier {
id = guidFor(this);
get config() {
return this.args.positional[0];
}
didInstall() {
this.element.id = this.id;
let config = th
this.tabulator = new Tabulator(`#${this.id}`, this.config);
}
}
And then maybe in a component or controller or something, you'd have something like:
export default class MyComponent extends Component {
get myConfig() {
return { ... };
}
}
<div {{tabulator this.myConfig}}></div>
and that should be it.
You'll want to import the CSS in your app.css

Allow binding to any data-* attribute on an ember component

I am designing a library whereby I would like to allow the user to supply any data attributes they might like.
{{my-component data-type='hello' data-name='world'}}
I don't know ahead of time which data attributes they might like to bind to so can't add them to the attributeBindings array.
Is there a workaround for this?
Use the didReceiveAtts hook of your component:
didReceiveAttrs(params){
let newAttrs = params.newAttrs;
let attributeBindings = Ember.A();
Object.keys(newAttrs).forEach((attr)=>{
if(attr.indexOf('data-')>= 0){
attributeBindings.pushObject(attr);
}
});
this.set('attributeBindings', attributeBindings);
}
Look that Sample twiddle
Updated, after deprecation:
Since arguments of didReceiveAttrs function are deprecated, you need to change the code as the following:
didReceiveAttrs(){
let attributeBindings = Ember.A();
Object.keys(this).forEach((attr)=>{
if(attr.indexOf('data-')>= 0){
attributeBindings.pushObject(attr);
}
});
this.set('attributeBindings', attributeBindings);
}
See updated twiddle.
I guess after v3.10 you can do this without any hacks with the angle bracket invocation (and if required pass further using the ...attributes). So in my simplest case it was as simple as
<MyComponent data-aaa="bbb"/>

Ember template helper get-value-with-key

Does Ember have any template helper "get-value-with-key"
I found the below usage, but not sure what it does exactly ?
{{get-value-with-key item optionValuePath}}
There is an Ember Get Helper for HTMLBars.
You might have to install the Package "ember-get-helper" if you are on ember < 2.1.
{{get object key}}
Let's say you have the following object:
var obj = {
"key1": {
"subkey1": "hello world"
}
}
Using Ember 3.18, to access "hello world" from the template, you can do:
{{get obj 'key1.subkey1'}}
You can use the build-in get helper. See docs here: Ember Docs.
Usage example:
{{get object key}}
Note that the get helper will not be able to work on all JavaScript keys. For example, a key with a '.' will not work with the built-in get helper.
For example, if you have a valid JavaScript object like:
const example = {
'example.pdf': 'pdf_url'
}
// You can access this key normally via
example['example.pdf']
however, this will not work in the get helper
{{get this.example 'example.pdf'}}
One solution is to create a helper that can support the types of keys you need to support. For example, I made a helper that can work on keys with a '.' by including '.' in the key name which are escaped like with ''.
{{get this.example 'example\.pdf'}}
The ember twiddle can be found here: twiddle
Other Helpful Sources:
Dot notation for object keys with dots in the name
How do I reference a field name that contains a dot in mustache template?

EmberJS: How to check if a component exists (inside another component)?

My goal is to resolve to a generic component is the component doesn't exists. It managed to do it like this:
// app/components/dynamic-widget.js
...
widgetName: function() {
var name = this.get('config.name');
if (!this.container.resolve('component:'+name)) {
name = 'generic-widget';
}
return name;
}.property('config.name')
...
Then in app/templates/components/dynamic-widget.hbs:
{{component widgetName}}
Then, I could use my dynamic-component like this:
{{dynamic-widget 'foo-widget'}}
If foo-widget is not implemented, it fallback into generic-widget.
But since EmberJS 1.11, resolving a component from a component's container is deprecated:
DEPRECATION: resolve should be called on the registry instead of the container
So my question is, how can I check if a component actually exists without using this.container.resolve ?
Thanks a lot.
Does this.container.registery.resolve work?
Looking at the code in git https://github.com/emberjs/ember.js/blob/5fd2d035b30aa9ebfe73de824b3b283ec8e589cc/packages/container/lib/registry.js
Looks like you may also be able to use this.container.registery.has

How can I include given Handlebars helpers in Ember

On Github, many Handlebars helpers are presented. You can find them here.
I'd like to use them, but have no Idea, how to include them. The code is looking as it's javascript (files are ending with .js ...), but words like 'import','export','default' are confusing me.
As I understand (guess), I have to include the if-condition.js at first. Later, the other (included) files refer to this file.
But when I do this, the console throws an Error:
Uncaught SyntaxError: Unexpected reserved word .
Has anyone an idea, how to get these codes working?
import and export are keywords for the upcoming module syntax in the next version of Javascript. You can use them today by using a transpiler to convert it to normal ES5 syntax.
However, if you're only using a few helpers, it's very easy to 'transpile' them by hand. Instead of exporting the function, just pass it to a Ember.Hanldebars.registerBoundHelper call. Here's the if-condition helper:
Ember.Handlebars.registerBoundHelper('if-condition', function() {
var args = [].slice.call(arguments);
var options = args.pop();
var context = (options.contexts && options.contexts[0]) || this;
if (!options.conditional) {
throw new Error("A conditional callback must be specified when using the if-condition helper");
}
// Gather all bound property names to pass in order to observe them
var properties = options.types.reduce(function(results, type, index) {
if (type === 'ID') {
results.push(args[index]);
}
return results;
}, []);
// Resolve actual values for all params to pass to the conditional callback
var normalizer = function() {
return Ember.Handlebars.resolveParams(context, args, options);
};
// This effectively makes the helper a bound helper
// NOTE: 'content' path is used so that multiple properties can be bound to using the `childProperties` argument,
// however this means that it can only be used with a controller that proxies values to the 'content' property
return Ember.Handlebars.bind.call(context, 'content', options, true, options.conditional, normalizer, properties);
});