Creating model attributes at run time in ember - ember.js

I am creating an ember application where i want to implement a table built with ember. I am able to create a table using ember-models-table,and i am already able to render the table with column list and data. Now the problem is that the column list is coming from server and in ember-data i need to put all the columns as attributes in a model in order to show the data, so can someone help me in loading the column list in ember model at the run time.
my contoller is as follows
self.get("store").query("user",{page:1,pageSize:10}).then(function (data)
{
self.set("data",data);
});
my user model is as follws
/* With this it works */
index: DS.attr("number"),
firstname: DS.attr("string"),
lastname: DS.attr("string"),
age: DS.attr("number"),
city: DS.attr("string")
/* but i want these attributes to be dynamic based on server response */

Ember Data exists to specify the data model, i.e. the structure of the different kinds of data types you have. This structure, i.e. the attrName: DS.attr('type') specifications, is necessary for ember-data to track when a model instance should be considered "changed" (namely when one of named attributes changed; user.set('foobar',42) will work as expected but not count as change of the user object if foobar is not among the model attributes) and which attributes to send to the server on .save() (in this case, not foobar).
If you have an API endpoint that returns differently structured data depending on query parameters or anything, you arguably do not, in fact, have a data model, and might be better off not using ember-data.
If I recall ember-models-table correctly, it does not need an array of model instances as data, so you might as well make Ajax calls directly instead of via the store, wrap the objects of your flexible "server response" in Ember.Objects and pass an array of them to ember-models-table (or try passing the plain old json object array, i.e. the server response directly, which might work even although the documentation does not suggest it).

Related

Ember data store handling array of objects

I'm new to ember and exploring its capabilities by building a small module. I came across a scenario where I need to update the ember model content synchronously. The ember-data model contains an array of objects as contents.
I was hoping to perform a few tasks as follows
Perform an array content reorder - for the sake of simplicity we
can assume swapping the first and last item.
Append a record
without a network call
Delete a record without a network call.
Doing these should automatically sync the data bindings/computed props
My data model after a peekAll call contains 10 records(shown below) on which I need to perform the above operations.
My model is as shown below
export default Model.extend({
testId: attr('number'),
name: attr('string')
});
What is the right approach to update the content record? Could someone please suggest how to proceed?
This looks to me like the results of running something like let arr = await store.findAll('test-model'), is that correct? This is probably a PromiseArray and you can access the data as a Javascript Array by calling arr.slice() on it. This will let you do normal array operations, though performing a content re-order doesn't really make much sense in this scenario. I assume you were using it as an example.
For adding and removing records without a network call you can do that by going back to the store and this is what is covered in the docs, you don't need to act on this Object you're looking at.
Adding a new record:
let testModel = store.createRecord('test-model', {
name: 'Lorem ipsum'
});
testModel.save(); //until you do this no network data will be sent
Removing a record:
let testModel = store.peekRecord('testModel', 1); //to get a record with ID of 1
testModel.deleteRecord();
testModel.save(); //until you run save no network is sent
Once you've taken action like this on the store the data structure you posted above may be updated to contain the new data depending on how you accessed it originally. You can also re-fetch data from the store which will now know about your adding a deleting of models (even though you haven't saved it back to the server yet)
If you haven't saved yet and you re-do a peekRecord you'll need to filter out any deleted records from the results.
let undeletedModels = this.store.peekAll('test-model').filter(m => !m.isDeleted);

Ember store.findAll is reloading view and store.query is not

At the moment, when an article is added to the store, my view is not updated when I use store.query(), filtering server side, in my route but it's updated when I use store.findAll() with filtering client side.
With findAll, filtering client side
//route.js
model() {
return this.get('store').findAll('article');
}
//controller.js
articleSorted: computed.filterBy('model', 'isPublished', true),
and with query filtering server side
//route.js
model() {
return this.get('store').query('article', { q: 'isPublished' }),
}
The fact is that findAll is reloading and query is not.
I've found this but did not understand
https://github.com/emberjs/ember.js/issues/15256
thanks for the question. I'll try to answer it the best I can but it would seem like some more documentation should be added to the Ember Guides to explain this situation 🤔
Essentially this.store.findAll() and this.store.query() do two very different things. findAll() is designed to be a representation of all of the entities (articles in your case) so it makes sense that the result will automatically update as the store finds more articles it should care about. It does this because it doesn't return an array of articles, it returns a DS.RecordArray that will automatically update.
query() on the other hand is designed to ask the backend every time what it expects the result to be, and you are usually passing a number of parameters to the query() call that the backend is using to find or filter results. It would be impossible for the frontend to know exactly how the backend interprets these query parameters so it is not possible for it to "auto-update" when a new article is added that would satisfy the same query.
Does that make sense? Would you like me to go into any more detail?
When using store.query to fetch data from the server, the view can still be auto-updated with new client-created store data before it's saved to the server, by using a "live" record array for it.
While data from store.query isn't live, data from store.peekAll is, so you can query first but then leverage store.peekAll for display. You can query before setting your model to the peeked data, or keep your query as the model but use some other property of peeked data for display. The important part is to ensure the query is resolved before peeking at the store.
Example based on the current code in your question:
// route.js
beforeModel() {
// using return ensures this hook waits for the promise to resolve before moving on
return this.store.query('article', { q: 'isPublished' });
}
model() {
// Queried server data should now be available to peek at locally,
// so we can set the model to a live version of it. Don't append filterBy here,
// otherwise it will no longer be live.
return this.store.peekAll('article');
}
// controller.js
// seemingly redundant filter since using query, but needed if there are other records
// in the store that shouldn't be displayed, and is recomputed when
// a record is added or removed from the store-based model
articleSorted: filterBy('model', 'isPublished', true) // filterBy imported from '#ember/object/computed'
// template.hbs
{{#each articleSorted as |article|}}
{{!-- this displayed list should update as new records are added to the store --}}
{{article}}
{{/each}}
Note that after a new record is saved to the server, the query can be updated via its update method or via a route refresh. This will re-run the query and get the updated results from the server. If the query is the model, that would look like model.update(). If it was saved to someOtherProperty, then someOtherProperty.update(). In either case, route.refresh() could be used instead to re-run all route hooks.
Some specific comments/examples that I think are helpful:
https://github.com/emberjs/ember.js/issues/15256#issuecomment-302894768
https://github.com/emberjs/ember.js/issues/15256#issuecomment-302906077
https://github.com/pouchdb-community/ember-pouch/issues/232#issuecomment-428927114

Ember js table component

I made a very simple table component. It takes a model, and builds a table from it.
http://emberjs.jsbin.com/raqomebeqi/1
Since its very primitive and has hard-coded property names, I decided to make an in-component representation of the model, to sort, filter the content of the table. It also allows to show just certain columns from the bounded model. (columns property)
http://emberjs.jsbin.com/raqomebeqi/2
The problem: It doesn't react to the changes of the model anymore. In the first example If I hit the 'change' button, it takes the first record, and set a new name. One can see the change in the table. In the second case Ember inspector shows the change of the name, but the table shows the old value.
I know I could do
data: Ember.computed('model.#each.name', function(){
//...
})
to monitor the changes on the name, but it is not very dynamic. How could I make my data react to each and every change on the model?
If you want your data to react on every change, you could do:
data: Ember.computed('model', function(){
//...
})
UPDATE:
This is not working

Ember Data: Retrieving Record Using store.get undefined

Before I get into the question, I should point out that I do not want to use the find operation to retrieve a record from the data store, I am attempting to access only the local storage without hitting the backend.
Okay, with that out of the way, I was looking at the ember docs which states the following:
/**
Update existing records in the store. Unlike push,
update will merge the new data properties with the existing
properties. This makes it safe to use with a subset of record
attributes. This method expects normalized data.
update is useful if your app broadcasts partial updates to
records.
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
store.get('person', 1).then(function(tom) {
tom.get('firstName'); // Tom
tom.get('lastName'); // Dale
var updateEvent = {id: 1, firstName: "TomHuda"};
store.update('person', updateEvent);
tom.get('firstName'); // TomHuda
tom.get('lastName'); // Dale
});
Now..my issue is when I try to do this:
store.get('person', 1).then(function(tom)
get is returning undefined even though when I use Ember Inspector, I can see the record inside the data store.
The happens for any objects I attempt to query in the store.
How do you use the store.get api?
The store's get method is inherited from Ember.Object, meaning that it has absolutely nothing to do with records. You're looking for the store's getById method.

How to save sort order back to the server with Ember Data

Say we have an ordered list of Todo items, the top one is the one we'll work on next:
App.TodoItem = DS.Model.extend({
title: DS.attr("string")
});
// comes back ordered from the server
var todos = App.TodoItem.find()
We then re-order the list via a drag & drop interface.
The API has an endpoint of /todos/sort which expects to be posted an array of IDs in their new order. This can be changed, but it's what we've got right now.
How would you go about saving the new order to the server?
I suppose I could add in a position attribute and update that on all the items when the order changes, then call commit on the store - but that will send all the data over the wire when all we want is the new ordering. Plus it would send lots of requests unless we implement batch saving.
Currently I'm thinking of bypassing Ember-Data for this part entirely and just have a plain old $.ajax request in a controller.
Create a model TodoList that contains metadata about the todos. One (i guess for now the only) attribute of that model would be the positions array. So when a user orders the list of todos there is no need to update each todos but instead update the container.