Assume I have a tasks application.
When the user updates a task an update button is enabled and the number of updated records is displayed. I do not manage to implement this when I delete a record.
For updates this is easily done in the controller:
App.TasksController = Ember.ArrayController.extend({
changedAmount: function(){
#filterBy('isDirty', true).get('length');
}.property('content.#each.isDirty');
})
But then when I delete a record using the deleteRecord function I don't manage to get the amount of changed/deleted records. Example:
App.TasksRoute = Ember.Route.extend({
.
.
actions: {
delete: function(task){
task.deleteRecord()
}
}
});
How can I access the deleted records or at least the amount of them?
My environment:
Ember : 1.3.1.1
Ember Data : 1.0.0-beta.6
Handlebars : 1.2.1
jQuery : 1.10.2
Looks like the property you're looking for is isDeleted. So you could modify that controller to be like:
App.TasksController = Ember.ArrayController.extend({
dirtyTasks: Ember.computed.filterBy('content', 'isDirty', true),
deletedTasks: Ember.computed.filterBy('content', 'isDeleted', true),
changedTasks: Ember.computed.union('dirtyTasks', 'deletedTasks')
});
Update:
I see what you mean about the content being autoupdated. I tracked this one down to the recordArrayManager being used by the Ember Store. When you issue either a find or a filter on the store, what is returned is a filtered record array, and one of the filters applied to that array is that none of the records in it can be in an isDeleted state. You can see that in action here.
Off the top of my head, I can't see a solution to this that I'm particularly satisfied with. But, if you absolutely need the content in your array controller to included deletedRecords, then you would need to override the method I linked to above in a custom RecordArrayManager class, and then set that recordArrayManager in the initialize for your application's store, as seen here.
Here's a jsbin demonstrating it
Note that this is a dirty hack and you really shouldn't do it. Frankly, I think a better solution would be to, instead of deleting the record using ember data methods, come up with your own flagged customIsDeleted state and set the model in that state instead of outright deleting it, if you really want it to stay on the screen.
Related
I am new to ember, so please treat me like a fool. What I'm trying to do first is to understand the concept.
In my application I heavily rely on few jQuery plugins they fetch new portion of data in their callbacks, that's how these plugins are designed, but I am not sure how can I trigger them to fetch a new portion of data from API passing to API updated query parameters after plugin has been rendered.
I have wrapped the plugin in a component, in component's template I send data to it as (I use emblem.js syntax here)
= plotly-chart chartData=model
In model I have
//app/models/data-points.js
import DS from 'ember-data';
export default DS.Model.extend({
// time: DS.attr(),
ch1: DS.attr(),
ch2: DS.attr(),
ch3: DS.attr(),
temperature: DS.attr(),
});
And then in component itself I fetch data
//app/components/plotly-chart.js
dataPoints: Ember.computed.map('chartData', function(item){
return item.getProperties('ch1', 'ch2', 'ch3', 'temperature');
}),
and make some manipulations with data, which isn't so important for the question itself.
Ah, and I have a route graph/ which later calls that component
//app/routes/graph.js
import Ember from 'ember';
export default Ember.Route.extend({
queryParams: {
start_timestamp: {
refreshModel: true
},
end_timestamp: {
refreshModel: true
}
},
model(params) {
return this.get('store').query('data-point', params);
}
});
So as you see I have tried to fetch new properties via query params, finally it works great if I just update the url in browser, but now can I trigger new call to API and fetch new data and get this new data in a component itself?
Also I'm struggling to understand what role controllers play in all of these. It is mentioned that controllers will be deprecated soon, but still used here https://guides.emberjs.com/v2.10.0/routing/query-params/
My code seems to work without controllers, so this is really confusing.
Also I suspect maybe I should use services for what I'm trying to achieve, but not sure how.
Ember experts, could you please point me into a right direction? The most important thing is how to fetch new portion of data from API with updated query parameters (query parameters to API itself, not nessesarely the ember application, but I suspect in ember-data it is the same thing? or not %) %) %)).
UPDATE 1
Really I should use services for that, shouldn't I? Pass arguments into a service and query a store there. Would that be a correct pattern?
Then query parameters in url are not the same as querying the store and is an independent thing. Am I right?
but how can I trigger new call to API and fetch new data and get this new data in a component itself?
If you change your queryParam values in a controller using an action (combined with your current route setup) it will adjust your route and re-call your API, as the values are bound together to make this particular use case simple :-) You're about 98% of the way there ... :-)
Re controllers going away, they won't for a long time as the replacement hasn't been worked out yet. You could do some of this in a service if you want to, but there is no need as you are almost done.
Thanks, that make sense though. I just worried I'm doing it wrong.
By they way finally I found a way to access store from the controller Access store from component but:
1. I was unable to take out the data from that variable, probably it's me being stupid.
2. I double it's the right way to access store directly in a component and better to use services for that or rely on “Data Down Actions Up” (DDAU) paradigm?
Finally I was able to fetch new portion of a data calling a controller's action from within the controller, but then the next problem raised - the data was updated, but the JS code did not know about that because I feed the jQuery plugin with this data and it did not pick up changes automatically. I think I might be doing it a wrong way there %)
But finally I get it working by adding an Ember's observer to that variable and in observer calling a redraw function (for chart in this particular place).
#acorncom Thanks!
--Using Ember Data 2.7.1--
I am trying to reverse the order of a collection of records without first turning them into an array using toArray(). This collection of objects comes from the promise returned by this.store.findAll('history-item').
I want to do this the ember way instead of making them plain javascript. I am getting a TypeError: internalModel.getRecord coming from record-array.js. For some reason when it is trying to do objectAtContent(), the content it is looking seems to not have a type. Through the stack trace I can see that the object I am dealing with is [Class], class being the history-item model. A few stack calls before the objectAtContent(), the object being dealt with switches from that history-item model to some other Class object that has no type attribute.
I am able to use Ember Inspector to see my data correctly, and if I just displayed the original collection of records on my template, it shows properly.
Has anyone run into this?
Some thoughts and considerations:
-Is there anything special about how findAll() works with its promise that doesn't allow for reversal since it is reloading in the background? I do want it to keep reloading live data.
-I am using ember-cli-mirage to mock my db and endpoints and I've follow the instructions to the letter I think. I am using an unconfigured JSONAPISerializer for mirage and and a unconfigured JSONAPIAdapter for ember. Could it have anything to do with metadata that is being sent from the back? Could it have something to with the models or records not being set up? Is there something special I have to do?
Route Segment that defines model and tries to reverse it:
[note: I know it may not be convention to prep the data (ordering) in the route but I just put it in here for ease of description. I usually do it outside in the controller or component]
model(){
return this.get('store').findAll('history-item').then(function(items){
return items.reverseObjects();
}).catch(failure);
History list model declaration:
export default DS.Model.extend({
question: DS.attr('string'),
answer: DS.attr('string')
});
Ember-Cli-Mirage config.js end points:
this.get('/history-items', (schema) => {
return schema.historyItems.all();
});
Ember-Cli-Mirage fixture for history-items:
export default [
{id: 1, question: "1is this working?", answer: "Of course!"}
}
Error:
TypeError: internalModel.getRecord coming from record-array.js
This issue also happens when I try to create a save a record. The save is successful but when the model gets reloaded (and tries to reverse), it fails with the same error. It doesn't matter if I the fixture or not.
Controller:
var newHistoryItem = this.store.createRecord('history-item', {
question: question,
answer: answer
});
newHistoryItem.save().then(success).catch(failure);
The result returned from store.findAll and store.query is an AdapterPopulatedRecordArray (live array), mutation methods like addObject,addObjects,removeObject,removeObjects,
unshiftObject,unshiftObjects,pushObject,pushObjects,reverseObjects,setObjects,shiftObject,clear,popObject,removeAt,removeObject,removeObjects,insertAt should not be used.
Have a look at corresponding discussion and
Proposed PR to throw error and suggestions to use toArray() to copy array instead of mutating.
I think using toArray is fine, no need to reinvent the wheel. Even Ember's enumerable/array methods are implemented using toArray under the hood.
I like keeping transforms on controllers/components, so Routes are only concerned with [URL -> data] logic. I think here I would keep the model hook returning the server data, and use a computed property on the controller:
import Ember from 'ember';
export default Ember.Controller.extend({
reversedItems: Ember.computed('model.[]', function() {
return this.get('model').toArray().reverse();
})
});
Twiddle: https://ember-twiddle.com/6527ef6d5f617449b8780148e7afe595?openFiles=controllers.application.js%2C
You could also use the reverse helper from Ember Composable Helpers and do it in the template:
{{#each (reverse model) as |item|}}
...
{{/each}}
I'm trying to update an app from Ember Data 1.0.0-beta.9 to 1.0.0-beta.11, and quite a bit seems to have changed. Specifically, I'm running into issues with finding out if a model instance does indeed have an associated model instance.
A = DS.Model.extend({
b: belongsTo('b', { async: true }),
});
B = DS.Model.extend({
a: belongsTo('a', { async: true }),
});
In Ember Data 1.0.0-beta.9, a.get('b') would simply return null if no associated model is found. That makes it easy to filter by computed property macros.
In Ember Data 1.0.0-beta.11, a.get('b') returns a promise, which makes it much harder to use in computed property macros. If the promise is fulfilled and the content of the promise is null, there's no associated record. But I have no idea whether it is possible to implement this check inside a Ember.computed.filter.
I have quite a few Ember.computed.filters probing quite a few Ember.isEmpty(a.get('b'))s, so I'm looking for a good way to check whether an object's async relationship is empty. Am I missing something obvious, like a built-in Ember Data api call? How would you implement such a check, if you need to filter by associated property presence/absence?
Well, to answer my own question, I got around this by filtering for Ember.isEmpty(a.get('b.id')), b/c I usually deal with persisted records and was able to ship around the edge cases. Sometimes it's so simple… :D
if the related record is just created and isNew is true, checking the id property would also return null. In my case this causes unexpected results. How I managed to overcome this is by checking the content property instead. For a newly created record this will return the model class, and null if the related object is empty.
I'm having some difficulty reloading my ember RESTful models, and I'm not sure why. Here's [conceptually] what I'm trying to do... http://jsbin.com/EfuBiNo/4/edit
The only difference between that code and my code is that I'm not using the FixtureAdapter, I'm using the RESTAdapter. Unfortunately, reloading my RESTful models is causing the number of records in the DS.RecordArray to double. So you can see the console is logging that (on every reload) there are two records in the RecordArray.
When I run this with my RestAdapter, the count goes 2...4...8...16....etc. So I'm not sure why it's doubling them every time, but if anybody has any insight on why -- or better yet, another way to reload these records -- I'd be very grateful. Thanks.
If you need to refresh a collection of records after you've already loaded them, you could do something like:
App.ThingsRoute = Ember.Route.extend({
model: function () {
return this.store.find('thing');
},
actions: {
refreshThings: function () {
var controller = this.controller;
this.store.find('thing').then(function (things) {
controller.set('content', things);
});
}
}
});
This will simply fetch all the things again and set the record array as the content on your controller whenever the promise resolves. If the items that come back are already catalogued in your store (the ids are already present) then you won't get a ton of duplicate records hanging around; stuff will just get updated. If there are new records that you didn't previously know about, then you'll get those now on your ThingsController.
This is also useful if you are doing some type of querying:
this.store.find('thing', {color: 'red'})
I have a backend resource that contains user activities and in the application I would like to present activities based on a single day's worth of activities. I have an ArrayController called ActivitiesController defined in the router like this:
this.resource('activities', { path: '/activities/:by_date' }, function() {
this.route('new');
});
The REST API provides the following GET method:
GET /activities/[by_date]
So far this looks pretty symmetrical and achievable but I'm running into two problems:
Parameterized array find. Typically a parameterized route would be serviced by a ObjectController but in this case the by_date parameter simply reduces/filters the array of activities but it's still an array that's returned. I'm not sure how to structure this in the model hook in the ActivitiesRoute so that its effectively doing a "findAll" rather than expecting a singular resultset.
Since functionality. As there is a reasonable network cost in bringing back these arrays of activities I would like to minimize this as much as possible and the REST API supports this by allowing for a since parameter to be passed along with the date of the last request. This way the server simply responds with a 304 code if no records have been updated since the last call and if there are new records only the new records are returned. Is there anyway to get this "out of the box" with ember-data? Does this require building a custom Adaptor? If so, are there any open source solutions that are available?
p.s. I was thinking that part of the answer to #2 might be to incorporate Alex Speller's Query Parameters: http://discuss.emberjs.com/t/query-string-support-in-ember-router/1962/48
What does your route's model hook look like? I am thinking something like this should work:
model: function(params) {
return this.store.find('activity', { by_date: params.by_date });
}