Ember.computed.sort property not updating - ember.js

I've been cracking my head for the last several days, trying to understand what am I doing wrong.
I'm implementing an infrastructure of lists for my app, which can include paging/infinite scroll/filtering/grouping/etc. The implementation is based on extending controllers (not array controllers, I want to be Ember 2.0 safe), with a content array property that holds the data.
I'm using Ember.computed.sort for the sorting, and it's working, but i have a strange behavior when i try to change the sorter. the sortedContent is not updating within the displayContent, even though the sortingDefinitions definitions are updated.
This causes a weird behaviour that it will only sort if I sort it twice, as if the sorting was asynchronous.
I am using Ember 1.5 (but it also happens on 1.8)
(attaching a snippet of code explaining my problem)
sortingDefinitions: function(){
var sortBy = this.get('sortBy');
var sortOrder = this.get('sortOrder') || 'asc';
if (_.isArray(sortBy)) {
return sortBy;
}
else {
return (sortBy ? [sortBy + ':' + sortOrder] : []);
}
}.property('sortBy', 'sortOrder'),
sortedContent: Ember.computed.sort('content', 'sortingDefinitions'),
displayContent: function() {
var that = this;
var sortBy = this.get('sortBy');
var sortOrder = this.get('sortOrder');
var list = (sortBy ? this.get('sortedContent') : this.get('content'));
var itemsPerPage = this.get('itemsPerPage');
var currentPage = this.get('currentPage');
var listItemModel = this.get('listItemModel');
return list.filter(function(item, index, enumerable){
return ((index >= (currentPage * itemsPerPage)) && (index < ((currentPage + 1) * itemsPerPage)));
}).map(function(item) {
var listItemModel = that.get('listItemModel');
if (listItemModel) {
return listItemModel.create(item);
}
else {
return item;
}
});
}.property('content.length', 'sortBy', 'sortOrder', 'currentPage', 'itemsPerPage')
Edit:
fixed by adding another dependency to the displayContent (sortedContent.[]):
displayContent: function() {
....
}.property('content.length', 'sortBy', 'sortOrder', 'currentPage', 'itemsPerPage' , 'sortedContent.[]')

Your sort function is watching the whole array sortingDefinitions instead of each element in the array. If the array changed to a string or some other variable it would update but not if an element in the array changes.
To ensure your computed property updates correctly, add a .[] to the end of the array so it looks like this: Ember.computed.sort('content', 'sortingDefinitions.[]')

Related

How can I distinct a complex object list in DART

I have one list of complex object. How can I distinct the list using their IDs?
I cant use toSet and similars, because the hashcode from the objects all are diferent.
1) Vanilla Dart
Loop through the list, adding IDs to a set as you go. Whenever you add an ID to the set that didn't already exist, add that element to a new list of distinct values.
void main() {
var list = [
Data('a'),
Data('a'),
Data('b'),
Data('c'),
];
var idSet = <String>{};
var distinct = <Data>[];
for (var d in list) {
if (idSet.add(d.id)) {
distinct.add(d);
}
}
}
class Data {
Data(this.id);
final String id;
}
2) Packages
Several packages exist that expand on default the Iterable utility methods, such as flinq or darq. They add a distinct method you can call to easily get a list of unique members of a list based on some property of the members.
import 'package:darq/darq.dart';
void main() {
var list = [
Data('a'),
Data('a'),
Data('b'),
Data('c'),
];
var distinct = list.distinct((d) => d.id).toList();
}
(Disclaimer, I am the maintainer of darq.)
Try to use this extension:
extension IterableExtension<T> on Iterable<T> {
Iterable<T> distinctBy(Object getCompareValue(T e)) {
var result = <T>[];
this.forEach((element) {
if (!result.any((x) => getCompareValue(x) == getCompareValue(element)))
result.add(element);
});
return result;
}
}
Using:
var distinctList = someList.distinctBy((x) => x.oid);
Or you can use a hash there.

How to search for a particular text from list of names using if else condition in protractor?

var totalList_grps = element.all(by.css('p.group-name-text'));
totalList_grps.getText().then(function(text){
console.log('Total list of joined groups : ' + text);
});
Tried the above code for printing list of group names.
Got Output :Total list of joined groups : Party,Innovation,capsLock,Gym,Sunrisers
AW,Big Boss.
Now i need to search for a particular name using if else condition and i tried the second set of code, but its not displaying any output not even a error.
totalList_grps.getText().then(function(itemList) {
expect(itemList).toContain('Big Boss');
});
Here is developers code
1) use by.cssContainingText():
var bigBoss = element(by.cssContainingText('p.group-name-text', 'Big Boss'));
// then you can call click(), getText(), getAttribute('') on found element as following:
bigBoss.click();
2) use elements.filter():
var bigBoss = element.all(by.css('p.group-name-text'))
.filter(function(it){
return it.getText().then(function(txt){
console.log('txt: ' + txt);
return txt === 'Big Boss' || txt.includes('Big Boss');
});
})
.first();
3) use await with combination of if/else
var allNames = element.all(by.css('p.group-name-text'));
var length = await allNames.count();
var matchedIndex = -1;
for(var i=0;i<length;i++) {
var name = await allNames.get(i).getText();
if (name === 'Big Boss' || name.includes('Big Boss')) {
matchedIndex = i;
console.log('matchedIndex = ' + matchedIndex);
break;
}
}
var bigBoss = allNames.get(matchedIndex);
We can implement option 3 without using await, but the code will be not easy readable and more complex than current.
FYI, If you want to use await/async, you need to disable protractor promise management (know as control flow). You can't use both in your code at same time.

Create multiple db entries based on a variable in Ember

I'm a new to Ember and am having a really hard time with this problem. I'm building an app that manages beer tap lists. Each bar has a different number of taps.
I take the number of taps in the new bar signup = orgTap
On the first login to the dashboard I want to have an empty line for each tap and a db entry created to correspond to each tap.
The code I have is:
export default Ember.Route.extend({
model(params) {
return this.get('store').findRecord('bis', params.bis_id);
},
actions: {
generateTapList(orgTap, id){
var store = this.store;
let tapOwner = this.get('store').peekRecord('bis', id);
const taps = this.get('orgTap');
let i = orgTap;
const newTap = store.createRecord('taplist', {
bis: tapOwner,
{{HOW DO I LOOP???}}
tap: "Tap" + i,
i = i - 1;
{{/HOW DO I LOOP???}}
});
newTap.save();
},
}
});
I have installed ember-truth-helpers thinking it may help with the loop.
Side Question -- Am I stupid for trying to learn Ember for my first app after a full stack dev class?? I feel that everything I just learned is irrelevant to how Ember works.
Solution:
generateTapList(orgTap, id){
var store = this.store;
let tapOwner = this.get('store').peekRecord('bis', id);
var tablistProps = {};
var i = 0;
tablistProps['bis'] = tapOwner;
while (i < orgTap){
i++;
tablistProps = {tap: "tap" + i, tapBeer: ""};
const newTap = store.createRecord('taplist', tablistProps)
newTap.save();
}
},

Ember js, get next item in model?

Is there any way to get the next item in model in ember.js?
something like:
var nextObjectAfterIdOne = this.store.getById(App.Model, '1').get('nextObject')
I came across this answer but it seems a bit hacky, and the IDs in my application aren't necessarily sequential, and the items are ordered by a different attribute.
Any ideas?
I'm not sure it's the good way to do it but at least it should work. Try to iterate over your list of item and once you find the current id, the next iteration will give you the next item.
var list = this.store.all(App.Model);
var id = 2;
var found = false;
var itemFound = undefined;
list.forEach(function(item, i) {
if (found) {
itemFound = item;
return true;
}
if (item.get('id') == id) found = true;
});
This has not been tested but you can try it.

#each iteration number in Ember.js or {{#index}}

According to this question, it was possible to do something like this with Handlebars rc1:
{{#each links}}
<li>{{#index}} - {{url}}</li>
{{/each}}
{{#index}} would basically give you the iteration index, which is really useful when creating tables.
When I try this with Ember.js rc3, I get an unexpected token error. Does this not work anymore? Did it ever work? Is there another way to get the iteration index?
It looks like it was possible. Can't get it to work with HBS RC3. Probably, is deprecated.
Here's a "hand written" HBS helper.
This can help you gettin the index with {{index}} and side by side you can know if the iteration in on first or last object of the Array with {{first}} and {{last}} respectively.
Ember.Handlebars.registerHelper("foreach", function(path, options) {
var ctx;
var helperName = 'foreach';
if (arguments.length === 4) {
Ember.assert("If you pass more than one argument to the foreach helper, it must be in the form #foreach foo in bar", arguments[1] === "in");
var keywordName = arguments[0];
options = arguments[3];
path = arguments[2];
helperName += ' ' + keywordName + ' in ' + path;
if (path === '') {
path = "this";
}
options.hash.keyword = keywordName;
} else if (arguments.length === 1) {
options = path;
path = 'this';
} else {
helperName += ' ' + path;
}
options.hash.dataSourceBinding = path;
// Set up emptyView as a metamorph with no tag
//options.hash.emptyViewClass = Ember._MetamorphView;
// can't rely on this default behavior when use strict
ctx = this || window;
var len = options.contexts[0][path].length;
options.helperName = options.helperName || helperName;
options.contexts[0][path].map(function(item, index) {
item.index = index;
item.first = index === 0;
item.last = index === len - 1;
})
if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) {
new GroupedEach(ctx, path, options).render();
} else {
return Ember.Handlebars.helpers.collection.call(ctx, Ember.Handlebars.EachView, options);
}
});
and this can be tested like
{{#foreach array}}
{{log index first last}}
{{/foreach}}
i had the same problem recently i finish by writing a bound helper and passing them objects via Binding for example item here is an ember DS.Store object and content is a 'content' of the controller. hope it
Ember.Handlebars.registerBoundHelper 'isPair', (content, options)->
item = options.hash.item
content_name = options.hash.content || 'content'
if #get(content_name).indexOf(item) % 2 == 0 then 'is-pair' else 'is-unpair'
and in you view you call it
{{isPair content itemBinding='order'}}
i don't know if it is what you looking for but it might give you some ideas how to use it in your project.
btw. Ember overwrites #each helper that's why there it no #index i suppose