I have a very simple template loop:
{{#each}}
{{title}}
{{/each}}
Fact is, my title can appear in my data multiple times (of course other parts of the record change) but I want to make sure that there are no duplicates for a given property (in this case title). Is there any way to put some logic inside the template to make sure to exclude duplicates?
I tried using an Handlebar Helper, but I really didn't make much progress with that.
You should put a computed property on the controller that's backing the template. Ember has a method uniq that will return the unique items in the array.
uniqueItems: function(){
return this.get('model').getEach('title').uniq();
}.property('model.#each.title')
http://emberjs.jsbin.com/IMOMoliB/5/edit
Related
I have two arrays NOVNoticeTypeName and NOVNumber, they both have the same number of elements, now I want to loop through one of them and print the values side by side as below:
{{#each v.NOVNoticeTypeName as |nntn index|}}
{({{v.NOVNoticeTypeName.[index]}} {{v.NOVNumber.[index]}})
{{/each}}
I understand looping is a mess in the Ember handlebars.
How can I achieve this?
First of all I don't share your opinion that looping is a mess in Ember templates. Would be great if you don't put such opinions as a facts. Especially if it comes without any argument.
What you want to achieve could be done with a combination of an {{each}} loop and a template helper. As your example already shows {{each}} loop yields the current value and index. You can't access an array element directly using the index as in JavaScript array[index] in an Ember template but you could achieve the same using a template helper. A template helper, lets call it {{object-at}} that gets an index as first and an array as second argument and returns array[index].
Lets see an example how that would work:
{{#let (array 'a' 'b' 'c') as |letters|}}
{{#let (array '1' '2' '3') as |numbers|}}
<ul>
{{#each letters as |letter index|}}
{{letter}} {{object-at index numbers}}
{{/each}}
</ul>
{{/let}}
{{/let}}
Such a template helper wouldn't be to complex. But you don't have to worry at all, cause it's already available as part of ember-composable-helpers.
I am checking if there is anyway that I can use the result of one helper function in another helper function and how can I use it, for example, I am looping through as below
{{#each v.NOVNumber as |vv iindex|}}
And then if I am getting another element as below, using the same index:
{{get v.NOVNoticeTypeName iindex}}
Can I use this element that we have got in a statement like below, to check if that is first or last element etc?
{{#if (isFirst v.NOVNumber vv)}}
You you can use helpers together to create more powerful functions. Ember Composable Helpers provides both a good pattern for using helpers together as well as the specific has-next and has-previous helpers that you need to know if an element is first of last.
An example using has-next
{{#if (has-next page pages)}}
<button>Next</button>
{{/if}}
I have an array that I can loop through using ng-for syntax. However, ultimately I want to access just a single element of that array. I cannot figure out how to do that.
In my component script I have
export class TableComponent {
elements: IElement[];
}
In my template, I am able to loop through the elements via
<ul>
<li *ngFor='let element of elements'>{{element.name}}</li>
</ul>
However, trying to access an item in the element array by secifically referencing an item utilizing
x {{elements[0].name}}x
does not seem to work.
The formatting in the template is pretty explicit, so I want to be able to access each element of the array explicitly in the template.
I am not understanding something basic....
2020 Edit :
{{elements?.[0].name}}
is the new way for the null check
Original answer :
{{elements[0].name}}
should just work. If you load elements async (from a server or similar) then Angular fails when it tries to update the binding before the response from the server arrived (which is usually the case). You should get an error message in the browser console though.
Try instead
{{elements && elements[0].name}}
Work around, use ngIf check the length. elements? means if elements is null, don't read the length property.
<div *ngIf="elements?.length">
{{elements[0].name}}
</div>
See twiddle here: https://ember-twiddle.com/2150099882893760cef237ff2bd22e85
Basically, in crit-service I create Ember Objects "Crits" and "Crit", and fill them with some data.
The crit-service is used by two different components, which basically do the same thing: display the Crits.
The problem is that the "change" buttons do not work. By debugging, I see that the values are changed, but the view is not updated. Why is this? Since Ember.Object is Observable, shouldn't setting a value notify the template? And most importantly, how can I get this to work?
P.S. I've seen a workaround by using Ember.A() instead of Objects. However, this would add boilerplate, as my data model is really objects and not arrays of key-value pairs.
This seems to be an issue with the {{#each-in}} helper which does not reload on changes. A quick fix is to use the {{get}} helper.
So instead of this:
{{#each-in obj as |key val|}}
{{key}}={{val}}
{{/each-in}}
Do this:
{{#each-in obj as |key|}}
{{key}}={{get obj key}}
{{/each-in}}
However, this will never work if you add additional properties.
here is a working twiddle with that solution.
Another solution that will always work is to call .rerender() on the component. This is save thanks to glimmer, which does only update the parts of the DOM that have changed. However, you would have to call it on your common root component of the two components, or on both components.
I have a HTML fragment that iterates over key, value collection. When I create an object and put some value in, then iterate trough that object via HTML fragment, all works perfectly.
However since I need keys in specific order, I'm using a Map instead of plain object. This time when I debug I can see that my insertion order was preserved, but for some reason the HTML fragment which iterates over collection doesn't seem to know how to do so. I see nothing on my screen when I use the map object, opposed to the regular object when I see unordered content
tr ng-repeat="(key, value) in rowTitlesValues"
Is how my HTML fragment looks like, when I switch rowTitlesValues back to object works again, what am I doing wrong, and how does one keep insertion order or how do I sort object so it's keys are in custom order?
From Angular reference on ng-repeat (link):
Iterating over object properties
It is possible to get ngRepeat to iterate over the properties of an object using the following syntax:
<div ng-repeat="(key, value) in myObj"> ... </div>
You need to be aware that the JavaScript specification does not define the order of keys returned for an object. (To mitigate this in Angular 1.3 the ngRepeat directive used to sort the keys alphabetically.)
Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser when running for key in myObj. It seems that browsers generally follow the strategy of providing keys in the order in which they were defined, [...]
If this is not desired, the recommended workaround is to convert your object into an array that is sorted into the order that you prefer before providing it to ngRepeat. You could do this with a filter such as toArrayFilter or implement a $watch on the object yourself.
Additionally, I do not think Angular 1.x knows how to iterate over a Map. I believe this line in the code proves it:
collectionKeys = [];
for (var itemKey in collection) { // iterates your object using `in`, not `of` or `Map.forEach()`
...
}
// ng-repeat then iterates the collectionKeys to create the DOM
So you will probably need to act as Angular docs suggest:
[...] convert your object into an array that is sorted into the order that you prefer before providing it to ngRepeat. You could do this with a filter such as toArrayFilter or implement a $watch on the object yourself.