Is there any way to get at what an Ember.js object really contains in the JavaScript console. If you do console.log(this), you will get almost the same data structure for almost any object, something like this:
That's not super helpful, and it gives you no idea what attributes are actually set on the object.
So far, the only way I've found to debug is to try and console.log(this.get('whatever')) for any conceivable name, but it's hard to guess what's available.
Is there any way to dig deeper into the object's data?
Ember provides several methods to help debug an object from console:
Object.toString prints identity of any ember object
App.Person = Em.Object.extend()
person = App.Person.create()
person.toString()
//=> "<App.Person:ember1024>"
Ember.inspect converts the object into a useful string description
var object = Ember.Object.create({
firstName: 'Hansi',
lastName: 'Hinterseer',
age: 58
});
console.log( Ember.inspect(object) );
// {__ember1331067974108_meta: [object Object] , firstName: Hansi , lastName: Hinterseer , age: 58}
Ember.keys returns all of the keys defined on an object or hash
console.log(Ember.keys(this));
There is also the App.__container__ object which, if you know what name your objects are registered to the ember app with, will allow you to grab any object you need while debugging from any environment.
A couple of examples are
App.__container__.lookup('store:main') # Gets the store
App.__container__.lookup('controller:blog') # Gets the blog controller
ember-chrome-devtools is a nice way to solve this problem now...
If you're trying to inspect an Ember Data record, you can call the serialize method on it from your console, this will give you the object as your external data source expects.
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 just stumbled on to Ember Data Polymorphic Relationships. I think they would be useful for what I am working on. Here is my main model. This model defines a has many relationship with topics thats both async and polymorphic.
App.Source = DS.Model.extend({
name: DS.attr('string'),
type: DS.attr('string'),
locationType: DS.attr('string'),
locationSpecific: DS.attr('boolean'),
primary: DS.attr('boolean'),
published: DS.attr('boolean'),
sort: DS.attr('number'),
topics: DS.hasMany('topic', {
async: true,
polymorphic: true
})
});
Next we have the Topics with a Base class being 'Topic'
App.Topic = DS.Model.extend({
name: DS.attr('string'),
sort: DS.attr('number'),
source: DS.belongsTo('source')
});
App.RegTopic = App.Topic.extend({
test: DS.attr('number', {
defaultValue: 8
}),
notes: DS.hasMany('notes', {
async: true
})
});
App.SummaryTopic = App.Topic.extend({
number: DS.attr('number', {
defaultValue: 9
})
});
and here is how I call to get the topics
App.TopicsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('source').get('topics');
}
});
When I list the sources, I get a list of the following objects back
{
id: 1
name: "test"
type: "testType"
locationType: "international"
locationSpecific: "true"
primary: true
published: true
sort: 1
links: {
topics: "/topics?sourceId=1"
}
}
then my topic call gets objects like these back
{
id: 4
sourceId: 1
name: Topic 4
type: "regTopic"
sort: 1
}
Am I missing something? Can you not use polymorphic relationships with the 'links' object?
From my understanding of the Polymorphic Relationship, when I make the call to /topics?sourceId=1 its should be essentially loading 2 different topics in one call so then I can display the regTopic and summaryTopic on the same page but in seperate lists and keep them as seperate objects?
Here is an updated jsbin, that seems close to working. http://emberjs.jsbin.com/medojitibo/1/edit?html,js,console,output
The issue I am seeing it that in my controller, the topics are all App.Topic in the list. There is no App.RegTopic or App.SummaryTopic
The links element looks like JSON-API, for which there is an addon. I have had to make modifications for polymorphism in my fork. It may be easier to modify your back-end to support the API specified by Ember Data.
I would not recommend using Ember Data polymorphism unless you really know what you are doing and have convinced yourself it does what you need. Currently it's designed for fairly limited use cases. There are a number of discussions and proposals on the topic that you can track down if you are interested.
You apparently believe, or the API believes, that the format for the links property in the JSON for the source object is an object containing a single (?) topics property which gives the API endpoint for retrieving a polymorphic topic. That's not how it works at all.
The correct syntax for the links property in the returned JSON would be an array of {id, type} tuplets:
{
id: 1
name: "test"
type: "testType"
...
links: [
{ id: 'topic1', type: 'regTopic'},
{ id: 'topic2', type: 'summaryTopic' }
]
}
The format of the data for the association, as shown above, is an array of id/type objects, rather than just an array of ids. This is required for Ember Data polymorphism to work. If you can't arrange for your back-end to produce exactly this kind of format, you will end up patching and hacking a number of methods in adapters and serializers and at the end of the day building your own version of polymorphism, probably not an endeavor you wish to embark on.
Without this special form of JSON for the association properties, Ember Data has no idea of what type of object to go looking for. With it, Ember Data will create the proper instance type of topic for each entry in this topics array, and essentially make a call to find('reg-topic', 'topic1') and find('summary-topic', 'topic2') to get its data. That means that you will also need a separate API endpoint for each subtype of topic. (Matters differ if the topics are embedded, an alternative I won't go into here.)
So far so good, perhaps. But this starts to break down quickly once you start to do more sophisticated things. Let's say you want to fetch/retrieve a topic with a particular ID. One might think that since "topics are polymorphic", you could just do this.store.get('topic', id). But you can't. The store holds RegTopics and SummaryTopics in separate places, and does not connect them with Topic. If you ask for a 'Topic', it does not know to look under different (sub-)models for instances already in the store. So you have to know in advance what the type is and ask the store for the record of that particular type, which sort of defeats the whole purpose of polymorphism. If you simply call the topics endpoint, Ember Data will create a new model instance for Topic--it has no idea that it is supposed to examine the type field of the returned JSON to govern what subtype to turn it into.
The point is that here topics are not polymorphic. In Ember Data polymorphism is not at the model level; it's at the association level. What is polymorphic here, in other words, is not the topic model itself; it's the associations held in the links property on the source model. In other words, Ember Data has no idea about a model itself being polymorphic; it just knows that particular association (hasMany or belongsTo) can hold polymorphic values. For some insights into what might be required to make Ember Data truly support polymorphic models, albeit in a slightly different context, take a look at https://github.com/Bestra/ember-data-sti-guide.
To answer your question in your comment, no, Ember Data polymorphism is not the best way to do what you want. Do not venture out into these uncharted waters. I'd adopt a strategy of "poor man's polymorphism", where your Topic model has a type field, and might or might have some different properties depending on the type. You can then sort and group and filter on type to your heart's content.
Quite new to Ember, I'm building an app and encounter a difficult problem.
MY SITUATION
I have a single page, which should display a list of elements, named "containers", and inside each container, I want to display several "contents".
For now, here is a very simplified version of my JS code (just to give you an idea):
App.ContainersRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('container');
}
});
App.Container = DS.Model.extend({
title: DS.attr('string'),
contents: DS.hasMany('content')
});
App.Content = DS.Model.extend({
[...]
});
I will pass on the template part, wich is not a problem here, it's quite classic.
So, this code is working, I have my list of containers, and a list of the propers contents for each of them.
MY PROBLEM = "A WHITE SCREEN OF DEATH"
Indeed, as I fetch my data from a server in JSON, for now, before Ember transitionning to my page, I have a wait the loading of all the data.
The problem is that, "containers" data is quite small and static, so my server can answer it in less than a second, but, for the "contents" part, it is very long (between 20 to 40 seconds), as my server needs to do a lot of work to recover these date from my DB.
It would be ok for me to wait this long if my page was already loaded (navigation + containers without the contents), but right now, I just have a white page during 30s with no idea except the console to know is everything is ok.
Of course, I could use a "loading bar" thanks to "beforeModel" in the Route, but I would very much prefer to have access at least to the UI.
FOR YOU, WHAT COULD BE A GOOD SOLUTION FOR THIS PROBLEM?
Thanks a lot in advance for your help :)
I'd make content's async and fetch that after the fact so you can give a more responsive look and feel.
App.Container = DS.Model.extend({
title: DS.attr('string'),
contents: DS.hasMany('content', {async: true})
});
App.Content = DS.Model.extend({
[...]
});
That means Ember Data will make a callback for that data when requested, and not expect it at the same time as the container data.
Example: http://emberjs.jsbin.com/OxIDiVU/1045/edit
Additionally you can add a loading route in the resource above your Containers resource which could give feedback about how your fetching data. http://emberjs.com/guides/routing/loading-and-error-substates/
Example: http://emberjs.jsbin.com/cerid/2/edit
Personally I like the async idea more though, show the fact that you have containers, but then show contents as loading/empty.
I'm a newbie to Ember Data and I've just switched from FIXTURE data to the RESTAdapter but I don't know enough about how to connect the model and the API's call signature. Specifically I'd like to be able to call an endpoint GET /activities/[:user_id]/[:by_date]. This would load an array of "Activity" objects but only those for a given date.
Router:
this.resource('activities', { path: '/activities' }, function() {
this.route('by_date', {path: '/:user_id/:by_date'});
});
Route:
App.ActivitiesByDateRoute = Ember.Route.extend({
serialize: function(activity) {
return {
userId: 1,
dateBy: "2013-07-01"
};
}
});
First off I tried to hard code the values for userId and dateBy making the adjustments to the Route above. Sadly that did not work. I think I understand why -- although I don't have a quick way to fix this -- but more disturbing for me was that when I manually put in the parameters into the URL: http://restful.service.com/api/activities/1/2013-07-01. The results are quite surprising to me:
Initially the debugging messages suggest a success:
This however, is not correct as no network requests are actually made
If you reload the browser, it will now go out and get the Activities but to my surprise it also goes out to find the specified user. Hmmm. That's ok, the user 1 is pulled back successfully.
The Activity, however, is just a GET /activities call which fails because this endpoint needs the user and date qualifier to work. Why weren't these included in the request?
I don't think ember data supports "composite keys" (which is essentially what you are trying to do). You'd probably need to build your own Ajax for that specific model and implement it in the model hook for that specific route. You can always fetch the data and sideload it into ember-data if you'd like to have an ember data model.