Sort computed property on an ArrayController - ember.js

I have the following jsbin: http://jsbin.com/okoxim/4/edit
filteredContent is a computed property which is filtering my Controller's content. I want to know how to sort the computed property and any ways I can improve the code I have.
App.StudentsController = Ember.ArrayController.extend({
sortProperties: ['name'],
nameFilter: null,
filteredContent: function(){
if(!this.get('nameFilter')) return this.get('content');
var nameRegEx = new RegExp(this.get('nameFilter'), 'i');
return this.filter(function(item) {
return item.get('name').search(nameRegEx) !== -1;
});
}.property('nameFilter', '#each.name')
});

Easiest way is to wrap the result in an ArrayProxy which sorts on the same sortProperties values. Something like,
filteredContent: function(){
var result;
if (!this.get('nameFilter')) {
result = this.get('content');
} else {
var nameRegEx = new RegExp(this.get('nameFilter'), 'i');
result = this.filter(function(item) {
return item.get('name').search(nameRegEx) !== -1;
});
}
var sortedResult = Em.ArrayProxy.createWithMixins(
Ember.SortableMixin,
{ content:result, sortProperties: this.sortProperties }
);
return sortedResult;
}.property('nameFilter', '#each.name')
Here's the updated jsbin.
Another way is to make filteredContent an explicit ArrayProxy and filter/sort on that.

Related

Filtering model data with Ember Enumerable methods

Full code:
http://jsbin.com/xabome/1/edit?html,js,output
I'm attempting to filter some model data by calling .filter on the record array returned from the store, but it has no effect.
My controller:
App.IndexController = Ember.Controller.extend({
fooDataOne: function() {
var fooData = this.get('fooData');
// If uncommented, I return both items
// return fooData;
return fooData.filter(function(item) {
console.log("Why am I not filtering?"); // Never called
if(item.get('id') == 1) { return true; }
});
}.property('fooData')
});
Is it not possible to call .filter as I'm wanting to here? What should I do instead?
The store.find method always returns a Promise.
So you could filter the data in a then block:
setupController: function(controller, model) {
this.store.find('test-data').then(function(data) {
var fooData = data.filter(function(item) {
return item.get('id') == 1;
});
controller.set('fooDataOne', fooData);
});
}
I've found an alternative solution by using an observer and getting a FilteredRecordArray from the store directly:
App.IndexController = Ember.Controller.extend({
fooDataFilter: function() {
var fooData = this.get('fooData');
var fooDataOne = this.store.filter('test-data', function(item) {
if (item.get('id') == 1) { return true; }
});
this.set('fooDataOne', fooDataOne);
}.observes('fooData').on('init')
});
Although the above works, it doesn't explain why the enumerable methods are not working on a a regular DS.RecordArray, which fooData should be.

Delay ember view render till $getJSON isLoaded

The problem with this code is that the render code is entered twice, and the buffer is not where I expect it. Even when I get the buffer, the stuff I push in is not rendered to the screen.
App.FilterView = Ember.View.extend({
init: function() {
var filter = this.get('filter');
this.set('content', App.ViewFilter.find(filter));
this._super();
},
render: function(buffer) {
var content = this.get('content');
if(!this.get('content.isLoaded')) { return; }
var keys = Object.keys(content.data);
keys.forEach(function(item) {
this.renderItem(buffer,content.data[item], item);
}, this);
}.observes('content.isLoaded'),
renderItem: function(buffer, item, key) {
buffer.push('<label for="' + key + '"> ' + item + '</label>');
}
});
And the App.ViewFilter.find()
App.ViewFilter = Ember.Object.extend();
App.ViewFilter.reopenClass({
find: function(o) {
var result = Ember.Object.create({
isLoaded: false,
data: ''
});
$.getJSON("http://localhost:3000/filter/" + o, function(response) {
result.set('data', response);
result.set('isLoaded', true);
});
return result;
}
});
I am getting the data I expect and once isLoaded triggers, everything runs, I am just not getting the HTML in my browser.
As it turns out the answer was close to what I had with using jquery then() on the $getJSON call. If you are new to promises, the documentation is not entirely straight forward. Here is what you need to know. You have to create an object outside the promise - that you will return immediately at the end and inside the promise you will have a function that updates that object once the data is returned. Like this:
App.Filter = Ember.Object.extend();
App.Filter.reopenClass({
find: function(o) {
var result = Ember.Object.create({
isLoaded: false,
data: Ember.Object.create()
});
$.getJSON("http://localhost:3000/filter/" + o).then(function(response) {
var controls = Em.A();
var keys = Ember.keys(response);
keys.forEach(function(key) {
controls.pushObject(App.FilterControl.create({
id: key,
label: response[key].label,
op: response[key].op,
content: response[key].content
})
);
});
result.set('data', controls);
result.set('isLoaded', true);
});
return result;
}
});
Whatever the function inside then(), is the callback routine that will be called once the data is returned. It needs to reference the object you created outside the $getJSON call and returned immediately. Then this works inside the view:
didInsertElement: function() {
if (this.get('content.isLoaded')) {
var model = this.get('content.data');
this.createFormView(model);
}
}.observes('content.isLoaded'),
createFormView: function(data) {
var self = this;
var filterController = App.FilterController.create({ model: data});
var filterView = Ember.View.create({
elementId: 'row-filter',
controller: filterController,
templateName: 'filter-form'
});
self.pushObject(filterView);
},
You can see a full app (and bit more complete/complicated) example here

Set computed property from another controller

I am trying to set the value of a computed property from one controller to another.
var BusinessOwner = Ember.ObjectController.extend({
actions: {
save: function(){
var self = this;
return Ember.$.ajax({
}).then(function(){
var ownerShow = self.store.getById('application',100);
ownerShow.get('ownerGeneral');
ownerShow.set('ownerGeneral', 'complete')
Ember.set(self, 'controllers.collectinfo.ownerGeneral','completed');
//self.set('controllers.collectinfo.ownerGeneral', "completed");
});
}
}
I have tried several different attempts at setting this property but have proved unsuccessful. If I use the self set, errors that I must use Ember.set(). If I use Ember.set() I get error collectinfo must be global if no obj given.
Thanks for any help
EDIT:
Thanks for looking at this. Yes I am includeing needs: 'collectinfo' I am still getting the error that Ember.set() needs to be used to set the object
You need to provide needs array in the controller as well.
var BusinessOwner = Ember.ObjectController.extend({
needs: 'collectinfo'
actions: {
save: function(){
var self = this;
return Ember.$.ajax({
}).then(function(){
var ownerShow = self.store.getById('application',100);
ownerShow.get('ownerGeneral');
ownerShow.set('ownerGeneral', 'complete')
Ember.set(self, 'controllers.collectinfo.ownerGeneral','completed');
//self.set('controllers.collectinfo.ownerGeneral', "completed");
});
}
}
Coding wise i suggest you create a own computed property for the one you want to access from other controller. So code becomes like this.
var BusinessOwner = Ember.ObjectController.extend({
needs: 'collectinfo',
ownerGeneral: Ember.computed.alias('controllers.collectinfo.ownerGeneral')
actions: {
save: function(){
var self = this;
return Ember.$.ajax({
}).then(function(){
var ownerShow = self.store.getById('application',100);
ownerShow.get('ownerGeneral');
ownerShow.set('ownerGeneral', 'complete')
Ember.set(self, 'ownerGeneral','completed');
//self.set('controllers.collectinfo.ownerGeneral', "completed");
});
}
}
You can set dependencies between controller with the controller needs property, it's documented at Ember Guide.
App.IndexController = Em.Controller.extend({
needs: 'application',
message: 'hi!',
actions: {
changeApplicationMessage: function() {
this.set('controllers.application.message', 'good bye');
},
changeMessage: function(){
this.set('message', 'bye');
}
}
});
The dependent controller property will be accesible in the controller at {{controllers.controllerName.propertyName}}
Demo: http://emberjs.jsbin.com/vevet/1/edit
In addition to what others said about "needs," just declare a shortcut variable for set and get:
var get = Ember.get;
var set = Ember.set;
and then use them like so:
set(object, 'property', 'value-to-set-property-to');
I assume that your controller declares a needs property with "collectInfo" as value? Then it should work this way:
var BusinessOwner = Ember.ObjectController.extend({
needs : ['collectInfo'],
actions: {
save: function(){
var collectinfoController = this.get('controllers.collectinfo');
return Ember.$.ajax({
}).then(function(){
var ownerShow = self.store.getById('application',100);
ownerShow.get('ownerGeneral');
ownerShow.set('ownerGeneral', 'complete')
collectinfoController.set('ownerGeneral','completed');
});
}
}

Emberjs how can I make collection arrangedContent and searchResults work together?

I have a controller that observes a search field like so:
Scrolls.IndexController = Ember.ArrayController.extend({
searchResult: function() {
var that = this;
this.get('model').set('content', this.store.filter('scroll', function(item) {
var searchTerm = that.get('searchCard');
var regExp = new RegExp(searchTerm, 'i');
return regExp.test(item.get('name'));
}));
}.observes('searchCard')
});
Which works great, but once I add a method that overrides arrangedContent to limit the returned items, it stops re-rendering.
Scrolls.IndexController = Ember.ArrayController.extend({
arrangedContent: Ember.computed('content', function() {
var count = 0;
return this.get('content').filter(function() {
count++;
return count <= 3;
});
}),
searchResult: function() {
var that = this;
this.get('model').set('content', this.store.filter('scroll', function(item) {
var searchTerm = that.get('searchCard');
var regExp = new RegExp(searchTerm, 'i');
return regExp.test(item.get('name'));
}));
}.observes('searchCard')
});
How can I get make what I'm doing to behave nicely with each other?
I see a few things here that jump out to me. First one being, in the context of a controller, content and model are the same thing so in the observer, when you do:
this.get('model').set('content'
You're setting a property of 'content' on the model when I think you actually intend to set the content directly on the controller, like this:
this.set('content',
I also kind of wonder whether you really need to override the content and arrangedContent properties (not sure what the calling code looks like). I suspect that might cause some bugs later. Instead, I wonder if you could set it up like this:
Scrolls.IndexController = Ember.ArrayController.extend({
firstThreeSearchResults: function() {
var count = 0;
return this.get('searchResults').filter(function() {
count++;
return count <= 3;
});
}.property('searchResults'),
searchResults: function() {
var searchTerm = this.get('searchCard');
return this.store.filter('scroll', function(item) {
var regExp = new RegExp(searchTerm, 'i');
return regExp.test(item.get('name'));
});
}.property('searchCard')
});
Final possible problem is the use of the filter function called on the store. According to the docs, this function: "returns a live RecordArray that remains up to date as new records are loaded into the store or created locally." The problem being, though the filter might update as new results are added, it might not cause the computed property that looks for the first three results to update. That is, the binding on that computed property might not fire. One way to get around this would be to do something like this:
Scrolls.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find();
}
});
Scrolls.IndexController = Ember.ArrayController.extend({
firstThreeSearchResults: function() {
var count = 0;
return this.get('searchResults').filter(function() {
count++;
return count <= 3;
});
}.property('searchResults'),
searchResults: function() {
var searchTerm = this.get('searchCard');
return this.get('content').filter(function(item) {
var regExp = new RegExp(searchTerm, 'i');
return regExp.test(item.get('name'));
});
}.property('searchCard', 'content.length')
});

Ember How fire observes when create object(observe was set already in model)

I cannot fire observes function when was created object in Controller Array
My code:
Model
App.Meeting = Em.Object.extend({
id: null,
name: null,
type: null,
proposes: null
});
App.Meeting.reopen({
proposedChanged: function() {
//some do
}.observes('proposes')
});
Controller
App.meetingsController = Ember.ArrayController.create({
content: [],
loadList: function(){
var me = this;
$.getJSON(url,function(data){
if(data.status == 0){
$(data.meetings).each(function(index,value){
var m = App.Meeting.create(value)
me.pushObject(m);
});
}else{
alert('Error loading content');
}
});
},
});
App.meetingsController.loadList();
When i run application Controller has get JSON data and created App.Meeting with that data, but observer not fire
While I was creating a jsbin to play with #Darshan Sawardekar got it right, so now you have to answers to play with :)
The important code:
App.meetingsController = Ember.ArrayController.create({
content: [],
loadList: function(){
var me = this;
$.getJSON(url, function(data){
if(data.status == 0){
$(data.meetings).each(function(index, value){
var m = App.Meeting.create();
m.set('id', value.id);
m.set('name', value.name);
m.set('type', value.type);
m.set('proposes', value.proposes);
me.pushObject(m);
});
} else {
alert('Error loading content');
}
});
}
});
Hope it helps.
EDIT
See here for a working jsbin that shows the concept.
I think observers fire when you do meeting.set('proposes', 'value'). They don't fire inside a create call. You could modify your create to retouch proposes. This might work,
var m = App.Meeting.create(value);
m.set('proposes', value.proposes);