Ember - if lastItem in each - ember.js

I am trying to show a piece of markup under the very last item in an each in ember -- I tried something like #last - but it came back with an error
<ul>
{{#each people as |person index|}}
<li>Hello, {{person.name}}! You're number {{index}} in line</li>
{{/each}}
</ul>

Ember's {{each}} template helper does not provide an easy way to do so.
As you might figured out, you could use {{unless index}} to conditionally render stuff on the first object. This is working since index starts at zero and 0 is considered to be falsely.
You could combine ember-truth-helpers and ember-math-helpers to achieve something comparable for last element. Both are well maintained ember addons that provide a full set of template helpers.
Ember-truth-helpers provides a eq helper that adds support for equal comparison in templates. Ember-math-helpers provides a sub helper to subtract a value from another in a template. Combining both of them we could build a condition that will be true only for the last element:
{{#each items as |item index|}}
<li>
{{item}}
{{#unless index}}(first){{/unless}}
{{#if (eq index (sub items.length 1))}}(last){{/if}}
</li>
{{/each}}
You find an ember twiddle demonstrating the approach here: https://ember-twiddle.com/6ea05a2e9c884bd772eefabc91173d08?openFiles=templates.application.hbs%2C
It's even simpler if the array is an Ember.NativeArray. NativeArray of ember provides a lastObject property, which points to the last item in the array. You could use that one for equal comparison directly as pointed out by #Gaurav in a comment: {{#if (eq item items.lastObject)}}(last){{/if}} But be aware that this may give wrong results if the array has duplicated elements.

Related

Conditional Render in Loop

This is my first ever project with ember.
I'm trying to render a language-changer component.
So far it renders ok, but I would like to append a spacer after each language as long and it is not the last one .
It should look something like
DE | FR | EN ...etc where | is actually a div
This is what I tried...
<div class="col-4 text-right language-changer">
{{#each languages as |lang index|}}
{{#if lang.isCurrent}}
<span>{{lang.designation}}</span>
{{else}}
<button {{action "changeLanguage" lang.key}}>{{lang.designation}}
</button>
{{/if}}
{{#if (index < languages.length)}}
<div class="spacer">|</div>
{{/if}}
{{/each}}
</div>
I read, that the if only works on properties... but how can I evaluate based on the current loop index?
What should I use instead of
{{#if (index < languages.length)}}
Thanks for the help.
Ember uses a logic-less template syntax. It does not many comparison operators by default. Especially there is nothing like a greater than comparison operator. There is not even an equals one. Basically you can only check if a variable or expression is truthy or falsy.
You have two options for your use case:
Add the separator unless it's the first item. You can check for the first item cause the index will be 0, which is falsy.
Use the addon Ember Truth Helpers, which provide a wide set of commonly known comparison operators.
As an alternative you could also write your own template helper that does a less than comparison but that would be reinventing the wheel as Ember Truth Helpers ships with such a helper.

Displaying a tag only for one element in a list. ember.js.(cloaked-collection, discourse)

I have a source code(discourse) than I need to work with with ember.js. I am trying handle only one "post" (the first) in a list using cloaked-collection.
//topics.hbs
{{#unless model.postStream.loadingFilter}}
{{cloaked-collection itemViewClass="post"
defaultHeight="200"
content=postsToRender
slackRatio="15"
loadingHTML=""
preservesContext="true"
uncloakDefault="true"
offsetFixedTop="header"
offsetFixedBottom="#reply-control"}}
{{/unless}}
//post.hbs
//some code here.
//Then I want to insert <div class="uniw"></div> only on the first post
The question is: for the list of itemViewClass="post", how can I check if I am in the first "post"? so that i can insert a piece if code.
You should be able to use firstObject to get first object of a collection. Something like this:
{{items.firstObject}}
Inside each block you can check index:
{{#each people as |person index|}}
{{#if (is-equal index 0)}}
{{person}}
{{/if}}
{{/each}}
Note that is-equal is non-standard helper. You can get it from ember-truth-helpers addon or write it yourself

How to use a component from within a component in Ember

G'day all,
I'm trying to wrap a component with another to provide a simplified editing wrapper.
The component is to conditionally show either a label or a select component that allows the user to pick the right value.
I want to wrap the power-select component, and pass it's values through to the sub-component, so the page template component reference looks like this:
{{cm-select
title="Country ##"
options=countries
selected=selectedCountry
searchField="name"
action="selectCountry"
}}
"countries" is an array of country objects, and selectedCountry is one of those country objects.
The component template has the following:
<td>{{title}}</td>
<td>
{{#if edit}}
{{#power-select
options=options
selected=selected
searchField=searchField
as |object|}}
{{object.name}}
{{/power-select}}
{{else}}
<small>{{modelElement}}</small>
{{/if}}
</td>
Unfortunately the power-select component renders with an empty list of options.
I thought wrapping those parameters in handlebars might do the trick, but it seems that handlebars in handlebars isn't a valid syntax.
Does anyone have any ideas?
Thanks,
Andy
That should work, I created a twiddle for you, demonstrating your use case. You'll see I updated the your cm-select template to this:
{{title}} |
<button {{action 'toggleEdit'}}>Toggle Edit</button>
<br/>
{{#if edit}}
Search for a Item via {{searchField}}
<br/>
{{power-select
options=options
selected=selected
searchField=searchField
onSelect=(action "itemSelected")
}}
{{else}}
{{search-list
options=options
searchField=searchField
onSelect=(action "itemSelected")
}}
{{/if}}
Where you iterated over options for power-select in the cm-select component, I moved that down into the power-select template. It's better to try and encapsulate some functionality there, instead of having everything in cm-select. I wasn't sure what you had in mind with {{modelElement}} so I just demonstrate what it would look like, using two different components in cm-select.

EmberJS loop through array show key and value

Ok so I have a pretty simple array of objects that I retrieved with emberjs, now all I need it to do is have something along the lines of
{{#each array}}
{{value}}
{{/each}}
and have it so that the array is looped by each item, and each item then is looped by each value and I display all the values of that object, I am not sure how to get it to work.
I have used this helper here https://gist.github.com/strathmeyer/1371586 but it's no use, I have no idea where to start, it just seems like such a simple thing I don't understand why handlebars/ember doesn't support it, also does #key work at all with ember?
Thanks
Ember Handlebars doesn't support looping over objects.
To loop in an array you simply use
{{#each item in collection}}
{{item}}
{{/each}}
or
{{#each collection}}
{{this}}
{{/each}}

Empty vs. not-loaded Ember.js controller

The handlebars each helper is used to iterate over a list of items in a backing ArrayController.
We can use the following construct to do something with a list of items AND show alternative details when that list is empty:
{{#each item in controller}}
<!-- stuff goes here -->
{{else}}
<!-- other stuff goes here -->
{{/each}}
This is great, but what if we want to differentiate between empty and not loaded? I feel like this must be a fairly common use case, but I can't figure out how to approach it - I don't see anything in the guides. Any help?
For this use case, i just wrap the "each" with a "if", that test an additional argument 'loaded' on the model.
{{#if content.loaded}}
{{#each item in controller}}
<!-- stuff goes here -->
{{else}}
<!-- other stuff goes here -->
{{/each}}
{{/if}}
The 'loaded' is toggled to true when the ajax promise (or whatever you do) completes.
Hope it helps!
For me with Ember 1.8.1, Ember Data 1.0.0-beta11 and iterating over hasMany collection on a model loaded wasn't working, as well as isLoaded. The thing that worked was isFulfilled.