Handlebars looping over an Ember component simple property - ember.js

In an ember component how can I generate 6 things in the template, given a component property called num with value 6?
Do I have to create an array just for the purposes of this in the component? If so can someone advise the most reusable way to do this?
I think I would need to do this via a helper:
Ember.Handlebars.helper('highlight', function(value, options) {
var escaped = Handlebars.Utils.escapeExpression(value);
return new Ember.Handlebars.SafeString('<span class="highlight">' + escaped + '</span>');
});

So in my experience, this works well:
{{#each row in rows}}
//do something
{{/each}}
However, as you say, you'll probably need an array populated. I've used this with an array of Ember models to populate a table, but I'm sure you'll be able bend it to your purpose!

Related

Ember 2.8: Should I randomize an array as a handlebars helper or write a function in the server side?

I have a function that takes in an array and randomizes the values inside of it.
shuffle([1,2,3,4]) => [2,4,1,3];
Now, I have succesfully used this function in my server side code for my ember app like so:
mirage config.js file
this.get('/games/:id', function (db, request) {
var game = games.find((game) => request.params.id === game.id);
var cardsShuffled = shuffle(game.attributes.cards);
game.attributes.cards = cardsShuffled;
return {data: game};
});
And then rendering my handlebars view like so:
play.hbs
<div>
{{#each model.cards as |instance|}}
{{game-card symbol=instance.url}}
{{/each}}
</div>
But I was wondering if it is better to create some sort of handlebars helper? I'm new to ember, but it doesn't look too clean to me to have the backend do that kind of logic. So here's what I was thinking:
shuffle-array.js helper
import Ember from 'ember';
export function shuffleArray(array) {
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
};
export default Ember.Helper.helper(shuffleArray);
And then somehow use it like so (tried this. failed):
play.hbs (revised)
<div>
{{shuffle-array model.cards}}
{{#each model.cards as |instance|}}
{{game-card symbol=instance.url}}
{{/each}}
</div>
So I guess my question is two-fold: Is it acceptable/unnaceptable to have this kind of logic coming from the mirage backend? Also, what is the best way to have a handlebars helper implement this?
Is it acceptable/unnaceptable to have this kind of logic coming from the mirage backend?
If the logic isn't meant to be persistent, I'd say no.
Also, what is the best way to have a handlebars helper implement this?
I'd prefer a computed property on the relevant controller. Using this shuffle function, it'd be:
import Ember from 'ember';
export default Ember.Controller.extend({
shuffledCars: Ember.computed('model.cars.[]', function(){
return shuffle(this.get('model.cars'));
})
});
Acceptable this or not depends on your needs. As I can see from code, you are shuffling some cards for some game. It possible that you need to do such kind of things on backend because of some security concerns.
But doing it with helper is also good and acceptable (but user with abilities to code can read/manipulate data). A few notes about helpers:
Helper function have two parameters: array of unnamed parameters and a hash of named parameters. So in your code you will access first parameter as array[0]
You should use helpers like this:
{{#each (shuffle-array model.cards) as |instance|}}
{{game-card symbol=instance.url}}
{{/each}}
More about helpers
Computed properties, mentioned in other answer, also are good for such kind of things. Maybe even better, because helper will shuffle array on each rendering (i.e. when user navigates back and forth), while computed prop will do that only when original array changes.

EmberJS Template concatenate

How can I can concatenate strings( or how to add classes ) on templates on EmberJs?
ex.
<script type="text/x-handlebars">
// This div I want to add a class go, Is this the right way to do it?
<div class="fly {{isGo}}">Fly now</div>
// Or it's something like this?
<div class="fly "{{isGo}} >Fly now</div>
</script>
bind-attr used to be a good way of working around a limitation within Ember's rendering. Now with HTMLbars Ember has recommend that we move away from bind-attr as we have more powerful methods.
Ember 1.13 deprecated bind-attr in favor of the new syntax.
http://emberjs.com/deprecations/v1.x/#toc_bind-attr
Working example of the two proposed methods can be seen in action on ember twiddle ,here:
https://ember-twiddle.com/38f69f01d2fd994af3b0965f10882005?openFiles=templates.application.hbs%2C
Method 1
If you want to do the combination inside your handlebars template you could do something like:
<div class={{concat "fly " isGo}}>Fly now</div>
Method 2
otherwise use a computed property like:
flyingClass: Ember.computed('isGo', function() {
// return a string with 'fly' and the value of
// isGo. Will be updated if isGo changes.
// Array values are created with space delimited by
// ['className', 'anotherClassName', 'lastClastName'].join(' ');
// => "className anotherClassName lastClassName"
let going = this.get('isGo') ? 'going' : ''
return ['fly', going].join(' ');
})
and then in your handlebars template:
<div class={{flyingClass}}>Fly now</div>
The main difference between the two methods depends on how you want your separation of concerns. Right now it might be easier to just do Method 1, but as conditions get more complicated you could hide more of the work in the computed property.
There is a complete discussion of this in the Ember guide: http://emberjs.com/guides/templates/binding-element-class-names/
But you'd do it like this:
<div {{bind-attr class="isGo"}}>Fly now</div>
And in your controller:
App.MyController = Ember.ObjectController.extend({
flightIsAGo: true,
isGo: function() {
return "fly"+this.get('flightIsAGo') ? ' isGo' : '';
}.property('flightIsAGo')
}

Pass array to linkTo helper

I want to create a component which needs to generate dynamics links. I tried passing the link data as an array, but this does not work.
var user1 = get("store").find("user", 1);
var data = {link: ["users.show", user1], title: "User1"};
{{#link-to data.link}}{{data.title}}{{/link-to}}
This should be equal to
{{#link-to "users.show" 1}}{{data.title}}{{/link-to}}
How to generate fully dynamic links from a variable?
You can specify an array as params argument into a link-to helper. Similar to nickiaconis' answer answer, but with just the default {{link-to}} helper:
{{#link-to params=data.link}}{{data.title}}{{/link-to}}
...will render something like:
User1
(tested with Ember 2.3.0)
Ember 1.13.x
The LinkComponent, which is what the link-to helper creates for you, is exposed as -link-to. I've created an example of its use here: http://emberjs.jsbin.com/rinukefuqe/2/edit?html,js,output
{{#-link-to params=(unbound link) hasBlock="true"}}
{{title}}
{{/-link-to}}
The params attribute is what the link-to helper normally bundles your positional parameters onto, although you must use the unbound helper here because the LinkComponent expects params to be an array rather than a value binding object. Additionally, the determination of use as block or inline component is not built into components yet, so you must pass hasBlock="true" unless you include the link text as the first parameter in your array.
Ember ≤ 1.12.x
Although it is not done already, you can manually expose the LinkView component, which is the equivalent of the new LinkComponent.
App.XLinkToComponent = Ember.LinkView.extend();
Then use it like:
{{#x-link-to params=link}}
{{title}}
{{/x-link-to}}
Using unbound and hasBlock="true" are not necessary as the internal logic of LinkView differs from LinkComponent.
I think that isn't possible to pass an array, but you can pass each argument directlly, like the following:
route
var user1 = this.store.find('user', 1);
var data = { data: { link: "users.show", model: user1, title: "User1" } };
return data;
template
{{#link-to data.link data.model.id}}{{data.title}}{{/link-to}}
I hope it helps

Ember, my template doesn't update on property change

Why doesn't my template get updated when I change the property it is rendering? The documentation states that {{each}} is bindings-aware, but there's obviously something to it that I am not aware of.
Like everything in Handlebars, the {{#each}} helper is bindings-aware.
If your application adds a new item to the array, or removes an item,
the DOM will be updated without having to write any code.
Here's my controller
App.MaintemplateController = Ember.Controller.extend({
alist: ['foo', "bar"],
actions : {
addel: function(){
this.alist.push('xpto');
console.log(this.alist);
}
}
});
In my template I have the following code.
{{#each alist}}
<li>{{.}}</li>
{{/each}}
<button {{action 'addel'}}>Add element</button>
The data is properly rendered, and when I click the button it does append elements to the property, but the template doesn't refresh. Why? How do I keep it in sync with my data?
Your template is not updating because you are using plain vanilla javascript .push instead of the by ember provided .pushObject and .removeObject respectively. Ember by default extends the array prototype to make it play nicely in a binding aware environment.
So to make your template update you should do:
App.MaintemplateController = Ember.Controller.extend({
alist: ['foo', "bar"],
actions : {
addel: function(){
this.get('alist').pushObject('xpto');
console.log(this.get('alist'));
}
}
});
The important line here is this.get('alist').pushObject('xpto');
Furthermore you should always use .get() and .set() to access objects in ember otherwise the binding mechanism will not be aware of changes made to the object.
Hope it helps.

EmberJS - change how an object property is displayed (not tied to route's controller)

Within my Ember app, I have an object model, called Item, with a property 'price', which holds a float value, such as 20.5. When I use the property in a template {{price}}, I would like for it to be formatted to look like so: $20.50.
This object is not the model that is tied to the controller at the route, but rather an element in an Ember Array, called items, which is a property of the model at the route. So I have something like this in my template:
{{#each item in items}}
{{item.price}}
{{/each}}
The problem seems pretty simple, but I can't really find a solution. The idea is that I do not want to change the name of the property to get it to look the way I want, since I could make a computed property that formats the price property, but then I would have to use a new name in the templates.
What you could do is write your own custom handlebars helper.
For your use case it would look something like this:
Ember.Handlebars.registerBoundHelper('formatNumber', function(value) {
return "$" + value.toFixed(2);
});
And then use it in your templates this way:
{{#each item in items}}
{{formatNumber item.price}}
{{/each}}
Please see here for a working jsbin.
Hope it helps.