Hy and thanks for reading :
I have an issue with Ember.ArrayController and arrangedContent. The senario is as follow :
Items inside my arrayController can be modified by some actions.
My arrangedContent is filtred on some of those items properties.
Thus if an observed item property change the arrangedContent property should be refreshed
I achieve this by setting the property() of my arrangedContent with "content.#each.myproperty"
All works fine, except if i try to refresh the model from the route i then get an error message TypeError: Cannot read property 'destroy' of undefined
at ContainerView.extend.arrayWillChange
In some case it would work but duplicate the content every time the refresh() is triggered
with some minimal code it may become more clear ...
App.IndexRoute = Ember.Route.extend({
model : function(){
return [App.Cars.create({color : "red", model : "march"}),
App.Cars.create({color : "yellow", model : "alto"}),
App.Cars.create({color : "blue", model : "gundam"}) ];
},
actions : {
reload : function(){
this.refresh();
}
}
});
App.IndexController = Ember.ArrayController.extend({
arrangedContent : function(){
var data= this.get("content");
data=data.filter(function(elem){
return elem.get("color").match(new RegExp("el","gi"))!==null;
});
return data;
}.property("lenght","content.#each.color"),
actions : {
allYell :function(){
this.get("content").forEach(function(elem){
elem.set("color","yellow");
});
},
erase : function(){
if(this.get("length")>0){
this.get("content").removeAt(0);
}
}
}
});
a JSBin can be found here http://jsbin.com/yebopobetu/3/edit?html,js,console,output
I've never seen anyone recommend overriding arrangedContent. I honestly wouldn't recommend it.
App.IndexController = Ember.ArrayController.extend({
foos : function(){
var data= this.get("model");
return data.filter(function(elem){
return elem.get("color").match(new RegExp("el","gi"))!==null;
});
}.property("#each.color"),
actions : {
allYell :function(){
this.forEach(function(elem){
elem.set("color","yellow");
});
},
erase : function(){
if(this.get("length")>0){
this.removeAt(0);
}
}
}
});
http://jsbin.com/sivugecunu/1/edit
Related
I am trying to reload a model that has changed on the server. My code is as follows:
App.CustomersController = Ember.ArrayController.extend({
intervalId: undefined,
startRefreshing: function() {
var self = this;
if ( self.get( 'intervalId' ) ) {
return;
}
self.set( 'intervalId', setInterval( function() {
//self.get('model').update();
self.get('model').reload();
}, 30000 ) );
}
});
App.CustomersRoute = Ember.Route.extend({
model: function() {
return this.store.find('customer');
},
setupController: function( controller, model ){
this._super( controller, model );
controller.startRefreshing();
},
actions: {
reload: function() {
this.get('model' ).reload();
}
}
});
You can see that I have two mechanisms for reloading the data - one a timer, and also an action triggered by a button in the UI. The latter is exactly what is shown in the ember-data documentation here: http://emberjs.com/api/data/classes/DS.Model.html#method_reload
Neither works. I get undefined in both cases i.e. the model returned does not have a reload() method. update() sort of works, except it does not remove deleted records and it is not what is recommended in the documentation. What am I doing wrong here in trying to use reload?
My stack:
DEBUG: -------------------------------
DEBUG: Ember : 1.5.1+pre.07fafb84
DEBUG: Ember Data : 1.0.0-beta.7.f87cba88
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery : 1.11.0
DEBUG: -------------------------------
and I am using the following adapter in case that makes any difference:
App.Store = DS.Store.extend({
// Override the default adapter with the `DS.ActiveModelAdapter` which
// is built to work nicely with the ActiveModel::Serializers gem.
adapter: '-active-model'
});
reload exists on a record, not a collection.
You would need to iterate the collection and call reload on each record.
self.get('model').forEach(function(record){
record.reload();
});
But I'm guessing you don't want to waste the callbacks to the server. In this case I'd recommend returning a filter as your model, then make another call to the server for all records.
App.CustomersRoute = Ember.Route.extend({
model: function() {
this.store.find('customer');
return this.store.all('customer');
},
setupController: function( controller, model ){
this._super( controller, model );
controller.startRefreshing();
},
actions: {
reload: function() {
this.get('model' ).reload();
}
}
});
App.CustomersController = Ember.ArrayController.extend({
intervalId: undefined,
startRefreshing: function() {
var self = this;
if ( self.get( 'intervalId' ) ) {
return;
}
self.set( 'intervalId', setInterval( function() {
self.store.find('customer'); // get all customers again, updating the ones we have
}, 30000 ) );
}
});
My app has a page where I'm using the view to display the data from other template with my view like this :
<script type="text/x-handlebars" data-template-name="enquiry">
[...] // some other information display before
{{view App.EnquirySelectedVehicleView}}
</script>
<script type="text/x-handlebars" data-template-name="selectedVehicle">
// Here is my content
</script>
My map looks like this :
this.resource('enquiry', { path: '/enquiry/:enquiry_id'}, function() {
this.route('selectedVehicle');
});
After reading the doc I just did this in my view :
App.EnquirySelectedVehicleView = Ember.View.extend({
templateName: 'selectedVehicle'
});
So far so good, its showing the text from my template. But I need to return data from an ajax call in this template (selectedVehicle) automatically, like its fetching the data when you are on /enquiry/1/.
I've done this in my router :
App.EnquirySelectedVehicle = Ember.Object.extend({});
App.EnquirySelectedVehicleRoute = Ember.Route.extend({
model: function() {
console.log('DEBUG: SelectedVehicle Model');
App.SelectedVehicle.vehicleStock(this)
}
});
App.EnquirySelectedVehicle.reopenClass({
vehicleStock: function(that) {
console.log('DEBUG: Fetch vehicle stock');
// Here come the ajax call
}
});
But my issue is that route is never call.. How can I return some value from my selectedVehicleRoute when I'm on the /enquiry/1 page in a view template ? (not sure if I ask it correctly)
Thanks for the help !
[edit]
#Fanta : I think I begin to understand how I can do that :
App.EnquiryRoute = Ember.Route.extend({
beforeModel: function(transition) {
this.controllerFor('login').send('isSession', transition);
},
model: function(param) {
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
var modelData = {enquiry: {}, vehicleStock: {}};
Ember.$
.get(host + '/enquiry/' + param['enquiry_id'], function(data) {
console.log('DEBUG: Enquriry GET OK id = ' + param['enquiry_id']);
modelData.enquiry = data.enquiry;
Ember.$.get(host + '/vehiclestock/' + data.enquiry.VehicleStockId, function(data) {
console.log('DEBUG: VehicleStock GET OK id = ' + data.enquiry.VehicleStockId)
console.log(data);
modelData.vehicleStock = data.vehicleStock;
resolve(modelData);
});
});
});
return promise;
}
});
It seems to work, now I have to figure it out how to display my Object :) but thank you for your help, that actually make me resolve it by a different way !
For future reference, just go to https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md and you'll see two links, one to JSFiddle and one to a JSBin with the basic setup.
Are you sure the route is not being called ? I created a Fiddle, http://jsfiddle.net/NQKvy/817/ if you check the JS console, you'll see in the log:
DEBUG: SelectedVehicle Model
DEBUG: Fetch vehicle stock
I have an app that keeps multiple task lists. Each task list has multiple tasks. Each task has multiple comments.
After updating to the new Ember Data, I had to scrap my record creation code. Currently I have this, which doesn't work. Though it doesn't throw any errors, my model does not seem to be updating.
App.TaskController = Ember.ArrayController.extend({
needs : ['list'],
isEditing : false,
actions : {
addTask : function(){
var foo = this.store.createRecord('task', {
description : '',
list : this.get('content.id'),
comments : []
});
foo.save();
console.log('Task Created!');
},
edit : function(){
this.set('isEditing', true);
},
doneEditing : function(){
this.set('isEditing', false);
}
}
});
Does anyone know how to create a new task (as well as how to create new comments) in this context?
See fiddle here : http://jsfiddle.net/edchao/W6QWj/
Yes you need pushObject in the list controller.
I would do the addTask like this, now almost every method in ember data return a promise
App.TaskController = Ember.ArrayController.extend({
needs : ['list'],
listController:Ember.computed.alias('controllers.list'),
isEditing : false,
actions : {
addTask : function(){
var listId = this.get('listController.model.id'),
list = this,
store = this.get('store');
console.log('listId',listId);
store.find('task').then(function(tasks){
var newId = tasks.get('lastObject.id') + 1,
newTask = store.createRecord('task', {
id:newId,
description : '',
list : listId,
comments : []
});
newTask.save().then(function(newTaskSaved){
list.pushObject(newTaskSaved);
console.log('Task Created!');
});
});
},
edit : function(){
this.set('isEditing', true);
},
doneEditing : function(){
this.set('isEditing', false);
}
}
});
I think seerting id's properly is very important, here with fixtures I do with a find, but with the rest adapter would be the backend who assign the id and set it in the response
JSFiddle http://jsfiddle.net/W6QWj/2/
Okay, so I've answered my own question. I was missing the pushObject() method. Here is another way to do things. Although I'm not sure if it's the best practice since it does throw the error "Assertion failed: You can only add a 'list' record to this relationship "
App.ListController = Ember.ObjectController.extend({
actions:{
addTask : function(){
var foo = this.store.createRecord('task', {
description : '',
list : this.get('content.id'),
comments : []
});
this.get('tasks').pushObject(foo);
foo.save();
}
}
});
I have my router set like :
this.resource('analytics', {path: '/analytics'}, function(){
this.resource('analyticsRuns', {path: ':exerciseRunId/analyticsRuns'},function(){
this.resource('analyticsRun',{path: ':runId'});
});
});
I jump to 'analyticsRuns' route using :
this.transitionToRoute('analyticsRuns',{"exerciseRunId":this.get('selectedExerciseRun.id')});
And my AnalyticsRunsIndexRoute is defined as :
AS.AnalyticsRunsIndexRoute = Ember.Route.extend({
model : function(params) {
var store = this.get('store');
//console.log(params); //returns empty object
//var exerciseRunId = AS.Analytics.get('exerciseRunId');
exerciseRunId = 577;
if(!(exerciseRunId)){
this.transitionTo('analytics');
}
store.find('analyticsRun',{'exerciseRunId':exerciseRunId});
return store.filter('analyticsRun', function(analyticRun){
return analyticRun.get('exerciseRunId') == exerciseRunId;
});
},
setupController : function(controller,model){
this._super(controller,model);
this.controllerFor('analysisTemplates').set('model',controller.get('store').find('analysisTemplate'));
}
});
I was wondering if I could access ":exerciseRunId" value in the AnalyticsRunsIndexRoute. Currently there isnothing set when I check the params arguments passed to this routes' model. On refresh however, the parameter becomes available to the AnalyticsRunRoute but only on refresh. So do I have to play with stateManagement to get the parameter value? or is there simpler way to access it. Thanks.
SOLUTION :
Again lots of thanks to Jeremy for walking through this. Here is how I have set up things now :
I defied routes like :
AS.AnalyticsRunsRoute = Ember.Route.extend({
model : function(params) {
return params;
}
});
AS.AnalyticsRunsIndexRoute = Ember.Route.extend({
model : function(params) {
var parentModel = this.modelFor('analyticsRuns');
var exerciseRunId = AS.Analytics.get('exerciseRunId')||parentModel.exerciseRunId;
var store = this.get('store');
if(!(exerciseRunId)){
this.transitionTo('analytics');
}
store.find('analyticsRun',{'exerciseRunId':exerciseRunId});
return store.filter('analyticsRun', function(analyticRun){
return analyticRun.get('exerciseRunId') == exerciseRunId;
});
},
setupController : function(controller,model){
this._super(controller,model);
this.controllerFor('analysisTemplates').set('model',controller.get('store').find('analysisTemplate'));
}
});
When calling transitionToRoute you should be passing a live object.
this.transitionToRoute('analyticsRuns',this.get('selectedExerciseRun'));
When you transition from route to route the model hook is skipped so it's important that you pass live objects either in transitionToRoute or in a link-to.
[UPDATE] in response to a comment:
If selectedExcerciseRun is not a live object, then you'd need to instantiate a live object before transitioning. Something like this :
var runId = this.get('selectedExerciseRun.id');
var promise = store.find('analyticsRun',{'exerciseRunId':runId});
promise.then(function(analyticsRun){
this.transitionToRoute('analyticsRun',analyticsRun);
});
App.Router.map(function() {
this.resource('documents', { path: '/documents' }, function() {
this.route('edit', { path: ':document_id/edit' });
});
this.resource('documentsFiltered', { path: '/documents/:type_id' }, function() {
this.route('edit', { path: ':document_id/edit' });
this.route('new');
});
});
And this controller with a subview event that basically transitions to a filtered document
App.DocumentsController = Ember.ArrayController.extend({
subview: function(context) {
Ember.run.next(this, function() {
//window.location.hash = '#/documents/'+context.id;
return this.transitionTo('documentsFiltered', context);
});
},
});
My problem is that this code works fine when Hash of page is changed.
But when I run the above code NOT w/ the location.hash bit and w/ the Ember native transitionTo I get a cryptic
Uncaught TypeError: Object [object Object] has no method 'slice'
Any clues?
Thanks
UPDATE:
App.DocumentsFilteredRoute = Ember.Route.extend({
model: function(params) {
return App.Document.find({type_id: params.type_id});
},
});
{{#collection contentBinding="documents" tagName="ul" class="content-nav"}}
<li {{action subview this}}>{{this.nameOfType}}</li>
{{/collection}}
The problem is that your model hook is returning an array, while in your transitionTo you are using a single object. As a rule of thumb your calls to transitionTo should pass the same data structure that is returned by your model hook. Following this rule of thumb i would recommend to do the following:
App.DocumentsController = Ember.ArrayController.extend({
subview: function(document) {
var documents = App.Document.find({type_id: document.get("typeId")});
Ember.run.next(this, function() {
return this.transitionTo('documentsFiltered', documents);
});
}
});
Note: I assume that the type_id is stored in the attribute typeId. Maybe you need to adapt it according to your needs.