Filter array controller with array - ember.js

How can I filter the content in my array controller with an array?
I know I can do this for multiple filters:
App.ItemsController = Ember.ArrayController.extend({
filter: function() {
var content = this.get("content");
return content.filter(function(data) {
return (data.get("foo1")) &&
(data.get("foo2")) &&
(data.get("foo3"));
});
}
});
or with just one:
return content.filterBy("foo1");
But if I wanted to filter with an array how would I do that?
I imagine something like this:
var array = ["foo1", "foo2", "foo3"];
return content.filterBy(array);
But this obviously doesn't work.
I ask because I use up to 20 filters and it would be helpful if I could do it with an array.
If there is an "ember way" to do this that would help me a lot.

I'm not sure if I understood you correctly, but I'll give a try:
var filters = ["a", "b", "c"];
var array = [{a: 1, b: 2, c: 3}, {a: 2, b: 3}]
var result = array.filter(function(item) {
var isPresent = true;
filters.forEach(function(filter) {
if (!Ember.isPresent(item[filter])) {
isPresent = false;
}
});
return isPresent;
});
or if you use underscore or lodash:
var filters = ["a", "b", "c"];
var array = [{a: 1, b: 2, c: 3}, {a: 2, b: 3}]
var result = array.filter(function(item) {
return _.all(filters, function(filter) {
return Ember.isPresent(item[filter]);
});
});
and similarly if you want to use it with Ember structures, you can change item[filter] to item.get(filter).

If your filters are more complex than simple check for properties, you could create an array of functions and then check if every item passes:
App.IndexRoute = Ember.Route.extend
model: ->
[
Em.Object.create
color: 'red'
isReallyRed: false
Em.Object.create
color: 'red'
Em.Object.create
color: 'red'
isReallyRed: true
]
App.IndexController = Em.ArrayController.extend
init: ->
#_super()
Em.run.next #, 'filterContent'
filters: [
(item) ->
item.get('color') is 'red'
, (item) ->
item.get('isReallyRed')
]
filterContent: ->
filters = #get 'filters'
result = #filter (item) ->
pass = true
filters.forEach (filter) ->
if pass
pass = filter item
pass
#set 'content', result
Demo.

I decided to take some inspiration from andrusieczko and Daniels answer to filter it by object instead of array.
App.ItemsController = Ember.ArrayController.extend({
filterlist: {},
filters: function() {
var self = this;
var content = this;
var filterlist = this.get("filterlist");
var allfilters = ["a", "b", "c"];
// set filterlist if any
allfilters.forEach(function(filter){
if ( self.get(filter) ) {
var value = self.get(filter);
filterlist[filter] = value;
}
});
// if equal or higher return y. else n.
function equal(item, key) {
var data = item.get(key);
var thefilter = self.get(key);
if ( data >= thefilter ) {
return "y";
} else {
return "n";
}
};
filter = content.filter(function(item) {
var pass = "";
if ( jQuery.isEmptyObject(filterlist) ) {
return true;
} else {
$.each(filterlist, function(key, value){
var pass2 = pass;
return pass = pass2 + equal(item, key);
});
if ( pass.indexOf("n") === -1 ) {
return pass;
}
}
});
return filter;
}

Related

google maps marker load via ajax and infocontent

In my app I have a google map and I want to add many marker on it and so I load the data via ajax (I'm in Django). I have the probem with infocontent and the 'click' event on the marker load via ajax: When I do 'click' on the marker nothing happened. This is my code:
function initialize() {
var latlng = new google.maps.LatLng({{lat}}, {{long}});
var myOptions = {
zoom: 16,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("mapCanvas"), myOptions);
var contentString = '<div id="content">{{name}}<br><span style="font-size:10px;">{{road}}</span><br><span style="font-size:10px;">{{city}} ({{prov}})</span></div>';
var infowindow_strutt = new google.maps.InfoWindow({
content: contentString
});
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: '{{name}}'
});
google.maps.event.addListener(marker, 'click', function() {
infowindow_strutt.open(map,marker);
});
var markers = [];
var markers_strutt = [];
var markers_concor = [];
var icon_strutt = {
url: 'http://icons.iconarchive.com/icons/graphicloads/colorful-long-shadow/24/Home-icon.png'
};
var icon_concor = {
url: 'http://icons.iconarchive.com/icons/graphicloads/100-flat/24/home-icon.png'
};
var marker_concor
var marker_strutt
map.addListener('bounds_changed', function() {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(null);
}
var bounds = map.getBounds();
var southWest = bounds.getSouthWest();
var northEast = bounds.getNorthEast();
$.ajax({
url: '/api/get_marker/',
cache: false,
data: {
'fromlat': southWest.lat(),
'tolat': northEast.lat(),
'fromlng': southWest.lng(),
'tolng': northEast.lng()
},
dataType: 'json',
type: 'GET',
async: false,
success: function (data) {
if (data) {
$.each(data, function (i, item) {
if (item.type = 1) {
var marker_concor = new google.maps.Marker({
position: new google.maps.LatLng(item.lat, item.lng),
icon: icon_concor,
map: map,
draggable: false
});
} else if (item.type = 2) {
var marker_strutt = new google.maps.Marker({
position: new google.maps.LatLng(item.lat, item.lng),
icon: icon_strutt,
map: map,
draggable: false
});
//Create an infoWindow
var infowindow_strutt = new google.maps.InfoWindow();
//set the content of infoWindow (the name)
infowindow_strutt.setContent(item.name);
//add click listner to marker which will open infoWindow
map.addListener(marker_strutt, 'click', function() {
infowindow_strutt.open(marker_strutt); // click on marker opens info window
});
}
markers_concor.push(marker_concor);
markers_strutt.push(marker_strutt);
if (marker_strutt) {
marker_strutt.setMap(map);
}
if (marker_concor) {
marker_concor.setMap(map);
}
});
}
}
});
});
}
jQuery(document).ready(function(e) {
initialize();
});
Where is the issue? Thanks a lot
EDIT: View code
def view_get_marker(request):
id = 24
list_id = []
list_marker = []
list_id.append(id)
# type 1 data
A_list = tab_A.objects.filter(id_struttura=id).filter(lat__gt=request.GET.get('fromlat'), lat__lt=request.GET.get('tolat')).filter(lng__gt=request.GET.get('fromlng'), lng__lt=request.GET.get('tolng'))
for a in A_list:
list_marker.append([a.lat, a.lng, a.name, a.road, a.city, a.id, 1])
list_id.append(a.id)
# type 2 data
B_list = list(tab_B.objects.all().values_list('lat','lng','name','road','city','id').exclude(lng__isnull=True).exclude(id__in=lista_id).filter(lat__gt=request.GET.get('fromlat'), lat__lt=request.GET.get('tolat')).filter(lng__gt=request.GET.get('fromlng'), lng__lt=request.GET.get('tolng')))
for lat, lng, name, road, city, id in B_list:
list_marker.append([lat, lng, name, road, city, id, 2])
if request.is_ajax():
results = []
for row in list_marker:
h_json = {}
h_json['lat'] = row[0]
h_json['lng'] = row[1]
h_json['name'] = unicode(row[2])
h_json['road'] = unicode(row[3])
h_json['city'] = unicode(row[4])
h_json['id'] = row[5]
h_json['type'] = row[6]
results.append(h_json)
data = json.dumps(results)
else:
data = 'fail'
mimetype = 'application/json'
return HttpResponse(data, mimetype)
EDIT 2:
I found the solution. The problem was in the event 'click' declaration. The right way is this:
google.maps.event.addListener(marker_strutt, 'click', (function(marker_strutt, i) {
return function() {
infowindow.setContent('my content');
infowindow.open(map, marker_strutt);
}
})(marker_strutt, i));
In this way I have the popoup!

Typeahead not working for cyrillic

I'm using Typeahead version 0.10.5. It's working for english words but it's not working for each word written in cyrrilic. Some words written in cyrillic are shown, but others aren't. What is this due to?
I'm using it like this:
$('#edicode').typeahead({
source: function(query, process){
CallAPI("GET", "/companies/get/" + query + "/like", function (data) {
var sourcedata = new Array();
var jsonData = JSON.parse(data);
var count = 0;
$.each(jsonData, function(jsonId) {
sourcedata.push(jsonData[jsonId].CODE + ' / ' + jsonData[jsonId].NAME);
selectedItems[jsonData[jsonId].CODE] = JSON.stringify(jsonData[jsonId]);
count++;
});
if(count <= 0)
{
$('#company_name').val('');
$('#company_name').prop('readonly', false);
}
console.log(sourcedata);
return process(sourcedata);
});
},
updater: function (item) {
var info = item.split(" / ");
var company = jQuery.parseJSON(selectedItems[info[0]]);
$('#EDICode').val(company.CODE);
return company.CODE + '/ ' + company.NAME ;
},
name: 'Company',
displayKey: 'value',
minLength: 2,
maxItem: 15,
accent: true,
hint: true
}).blur(function(){
});
Took 1 hour to find:
open bootstrap-typeahead.js (not minified)
find:
matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase());
},
change to:
matcher: function (item) {
var x=item.toLowerCase().indexOf(this.query.toLowerCase());;
if(x==-1)
x=0;
return ~x
},

Ember calculated property is recalculated without its dependencies changed

I have an calculated property(named userFields) depending on another calculated property(named userList). When the object(it's a view) is initialised, I found the property "userFields" was calculated hundreds of times with "userList" calculated only once, according to the console log. I don't know why does this happened.
Here are my codes:
App.MainServiceInfoApplicationQueueUserGroupSelectView = Ember.View.extend({
templateName: require('templates/main/service/info/applicationqueues/user_group_select'),
userCollapse: false,
groupCollapse: false,
toggleUserCollapse: function() {
myview = this;
this.set('userCollapse', !this.get('userCollapse'));
},
toggleGroupCollapse: function() {
this.set('groupCollapse', !this.get('groupCollapse'));
},
userList: function() {
console.log('calc user list'); // This is printed only once
var list = [];
App.User.find().forEach(function(item) {
list.push(item);
});
return list;
}.property('App.User.find()'),
groupList: function() {
var list = [];
App.Resourcegroup.find().forEach(function(item) {
list.push(item);
});
return list;
}.property('App.Resourcegroup.find()'),
userFields: function() {
console.log('calc user fields'); // This is printed hundreds of times
// Parse value
var parsedValues = [];
try {
var usersFromValue = this.get('value').split(' ')[0].split('/');
for (var i = 0; i < usersFromValue.length; i++)
parsedValues.push(usersFromValue[i]);
} catch (e) {}
var fields = [];
for (var i = 0; i < this.get('userList').length; i++) {
var item = this.get('userList')[i];
fields.push(Ember.Object.create({
viewClass: Ember.Checkbox.extend({
value: false,
checkedBinding: 'value'
}),
displayName: item.get('userName'),
name: item.get('userName'),
value: parsedValues.contains(item.get('userName'))
}));
}
return fields;
}.property('userList'),
groupFields: function() {
// Parse value
var parsedValues = [];
try {
var groupsFromValue = this.get('value').split(' ')[1].split('/');
for (var i = 0; i < groupsFromValue.length; i++)
parsedValues.push(groupsFromValue[i]);
} catch (e) {}
var fields = [];
for (var i = 0; i < this.get('groupList').length; i++) {
var item = this.get('groupList')[i];
fields.push(Ember.Object.create({
viewClass: Ember.Checkbox.extend({
value: false,
checkedBinding: 'value'
}),
displayName: item.get('groupName'),
name: item.get('groupName'),
value: parsedValues.contains(item.get('groupName'))
}));
}
return fields;
}.property('groupList'),
users: function() {
console.log('calc users'); // This is printed as many times as 'calc user fields' was printed
var list = [];
for (var i = 0; i < this.get('userFields').length; i++) {
var theField = this.get('userFields')[i];
if (parseBoolean(theField.get('value')) == true) list.push(theField.get('name'));
}
return list;
}.property('userFields, userFields.#each.value'),
groups: function() {
var list = [];
for (var i = 0; i < this.get('groupFields').length; i++) {
var theField = this.get('groupFields')[i];
if (parseBoolean(theField.get('value')) == true) list.push(theField.get('name'));
}
return list;
}.property('groupFields, groupFields.#each.value'),
usersString: function() {
return this.get('users').join(', ');
}.property('users'),
groupsString: function() {
return this.get('groups').join(', ');
}.property('groups'),
usersValue: function() {
return this.get('users').join('/');
}.property('users'),
groupsValue: function() {
return this.get('groups').join('/');
}.property('groups'),
value: function() {
return this.get('usersValue') + " " + this.get('groupsValue');
}.property('usersValue', 'groupsValue')
});

Ember-table integration with Ember-model / Ember-data

I am trying to link ember-models to the ember-table to pull paginated records from the server and add them to the table when scrolling down.
I can get it working by just requesting my api url with page number like in the ajax example on http://addepar.github.io/ember-table/ but i cant figure out how to integrate it with ember-model to create and ember objects and then add them to the table.
Here is my code to just make an ajax request and add to table. Can anyone tell me how i can change this to use ember-model / ember-data instead.
App.TableAjaxExample = Ember.Namespace.create()
App.TableAjaxExample.LazyDataSource = Ember.ArrayProxy.extend
createGithubEvent: (row, event) ->
row.set 'id', event.id
row.set 'name', event.name
row.set 'isLoaded', yes
requestGithubEvent: (page) ->
content = #get 'content'
start = (page - 1) * 30
end = start + 30
per_page = 40
# something like this ???
#App.Detail.find(type: 'companies', page: page, per_page: per_page).on 'didLoad', ->
url = "http:/myurl.dev/admin/details.json?type=companies&page=#{page}&per_page=30"
Ember.$.getJSON url, (json) =>
json.details.forEach (event, index) =>
row = content[start + index]
#createGithubEvent row, event
[start...end].forEach (index) ->
content[index] = Ember.Object.create eventId: index, isLoaded: no
objectAt: (index) ->
content = #get 'content'
#if index is content.get('length') - 1
# content.pushObjects(new Array(30))
row = content[index]
return row if row and not row.get('error')
#requestGithubEvent Math.floor(index / 30 + 1)
content[index]
App.TableAjaxExample.TableController =
Ember.Table.TableController.extend
hasHeader: yes
hasFooter: no
numFixedColumns: 0
numRows: 21054
rowHeight: 35
columns: Ember.computed ->
columnNames = ['id', 'name']
columns = columnNames.map (key, index) ->
Ember.Table.ColumnDefinition.create
columnWidth: 150
headerCellName: key.w()
contentPath: key
columns
.property()
content: Ember.computed ->
App.TableAjaxExample.LazyDataSource.create
content: new Array(#get('numRows'))
.property 'numRows'
Is the possible or does this slow it down to much?
Thanks for the help.
Rick
Here's a JSBin that I got working with Ember Data and the RESTAdapter: http://jsbin.com/eVOgUrE/3/edit
It works very similarly to the AJAX loading example, but uses Ember Data to load the data. I created a RowProxy object that is returned immediately to the Ember Table so that it can render a row. After Ember Data loads a page full of data it sets the object property on the RowProxy which updates the view.
window.App = Ember.Application.create();
// The main model that will be loaded into Ember Table
App.Gallery = DS.Model.extend({
name: DS.attr('string'),
smallUrl: DS.attr('string')
});
// This is a temporary buffer object that sits between
// Ember Table and the model object (Gallery, in this case).
App.RowProxy = Ember.Object.extend({
object:null,
getObjectProperty : function(prop){
var obj = this.get('object');
if(obj){ console.log(prop + " : " + obj.get(prop)); }
return obj ? obj.get(prop) : 'loading...';
},
isLoaded : function(){ return !!this.get('object'); }.property('object'),
name : function(){ return this.getObjectProperty('name'); }.property('object.name'),
id : function(){ return this.getObjectProperty('id'); }.property('object.id'),
smallUrl : function(){ return this.getObjectProperty('smallUrl'); }.property('object.smallUrl')
});
App.ApplicationController = Ember.Controller.extend({
tableController: Ember.computed(function() {
return Ember.get('App.TableAjaxExample.TableController').create({
// We need to pass in the store so that the table can use it
store : this.get('store')
});
})
});
App.TableAjaxExample = Ember.Namespace.create();
App.TableAjaxExample.ImageTableCell = Ember.Table.TableCell.extend({
templateName: 'img-table-cell',
classNames: 'img-table-cell'
});
App.TableAjaxExample.LazyDataSource = Ember.ArrayProxy.extend({
requestPage : function(page){
var content, end, start, url, _i, _results,
_this = this;
content = this.get('content');
start = (page - 1) * 3;
end = start + 3;
// Find galleries and then update the RowProxy to hold a gallery as 'object'
this.get('store').find('gallery',{page_size:3,page:page}).then(function(galleries){
return galleries.forEach(function(gallery, index) {
var position = start + index;
content[position].set('object',gallery);
});
});
// Fill the 'content' array with RowProxy objects
// Taken from the 'requestGithubEvent' method of the original example
return (function() {
_results = [];
for (var _i = start; start <= end ? _i < end : _i > end; start <= end ? _i++ : _i--){ _results.push(_i); }
return _results;
}).apply(this).forEach(function(index) {
return content[index] = App.RowProxy.create({
index: index
});
});
},
objectAt: function(index) {
var content, row;
content = this.get('content');
row = content[index];
if (row && !row.get('error')) {
return row;
}
this.requestPage(Math.floor(index / 3 + 1));
return content[index];
}
});
App.TableAjaxExample.TableController = Ember.Table.TableController.extend({
hasHeader: true,
hasFooter: false,
numFixedColumns: 0,
numRows: 19,
rowHeight: 35,
columns: Ember.computed(function() {
var avatar, columnNames, columns;
avatar = Ember.Table.ColumnDefinition.create({
columnWidth: 80,
headerCellName: 'smallUrl',
tableCellViewClass: 'App.TableAjaxExample.ImageTableCell',
contentPath: 'smallUrl'
});
columnNames = ['id', 'name'];
columns = columnNames.map(function(key, index) {
return Ember.Table.ColumnDefinition.create({
columnWidth: 150,
headerCellName: key.w(),
contentPath: key
});
});
columns.unshift(avatar);
return columns;
}).property(),
content: Ember.computed(function() {
return App.TableAjaxExample.LazyDataSource.create({
content: new Array(this.get('numRows')),
store : this.get('store')
});
}).property('numRows')
});
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://files.cloudhdr.com/api/v1/public',
// This is here to use underscores in API params
pathForType: function(type) {
var underscored = Ember.String.underscore(type);
return Ember.String.pluralize(underscored);
}
});
// Everything below is all here to use underscores in API params.
// You may or may not need this.
DS.RESTSerializer.reopen({
modelTypeFromRoot: function(root) {
console.log("modelTypeFromRoot " + root);
var camelized = Ember.String.camelize(root);
return Ember.String.singularize(camelized);
}
});
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalize: function(type, hash, property) {
var normalized = {}, normalizedProp;
for (var prop in hash) {
if (prop.substr(-3) === '_id') {
// belongsTo relationships
normalizedProp = prop.slice(0, -3);
} else if (prop.substr(-4) === '_ids') {
// hasMany relationship
normalizedProp = Ember.String.pluralize(prop.slice(0, -4));
} else {
// regualarAttribute
normalizedProp = prop;
}
normalizedProp = Ember.String.camelize(normalizedProp);
normalized[normalizedProp] = hash[prop];
}
return this._super(type, normalized, property);
}
});

can I change rest model state without using .set()

I have model with custom attribute(array of objects). Like this
App.Adapter.registerTransform('images', {
serialize: function(value) {
var ret = []
value.forEach(function(img){
ret.pushObject(img.get('uuid'))
})
if (ret.get('length')) {
return ret.join(',')
} else
return false
},
deserialize: function(value) {
ret = []
if (typeof value !== 'undefined') {
uuids = value.split(',')
for (var i = 0; i < uuids.length; i++) {
var id = uuids[i]
ret.pushObject( App.Image.create({'uuid': id}) )
}
}
return ret
}
})
And my model.
App.Item = DS.Model.extend({
…
images: DS.attr('images')
})
in controller I need commit data, after pushing changes in this property. What I need to do for this case?
uploadImage: function(){
var self = this
uploading.done(function(result) {
self.get('images').pushObject(App.Image.create({uuid:result.uuid}))
console.log(self.get('isDirty')) // false
self.get('store').commit() //nothing to change
}).fail(function(result) {
…
}).always(function() {
…
})
},
Have you tried to do this?
self.notifyPropertyChange('images');