Series: Introduction to Ember Data 2.0

Ember Data 2.0-Metaprogramming with DS.Model Attributes Property

Published on Dec 18, 2015

In this video we learn how to display all the attributes of several different models using the same route and template.

This amazing feat is made possible using the DS.Model attributes property and metaprogramming. Near the end we go over many other properties on DS.Model that lend themselves well to metaprogramming.


Links

Code

The attributes property returns an array of hashes that tell you about the attributes on a DS.Model. Note that you must have access to the model class and not just the model instance (it can be accessed by calling constructor on the model instance).

model.constructor.attributes
//=> [{type: 'string', name: 'name', ...}, {type: 'number', name: 'level', ...}]

In the context of the app, it can be used like this:

{{#each model.constructor.attributes as |attribute|}}
  {{attribute.name}}: {{get model attribute.name}}
  <br>
{{/each}}

You can also get the constructor in other ways. For example, you can get the modelType from the data adapter:

model(params){
  let modelObject = this.container.lookup('data-adapter:main').getModelTypes().findBy('name', params.model_name);
  let records = this.store.findAll(params.model_name)
  return {
    modelObject: modelObject,
    records: records
  }
}

Then you can call .klass on it to get the constructor, and find the attributes from there. In the following code you can also see us using the type property on the attributes.

{{#each model.modelObject.klass.attributes as |attr|}}
  <br>
  {{attr.name}}: {{attr.type}}
{{/each}}

Transcript

If you’ve been watching a while, you may remember how much I love metaprogramming. So an opportunity came up to do it with ds.model and I couldn’t resist, and I hope you enjoy it as well.

Here’s what we’re going to be making today. So notice that we have all the available models here, and when you click here, it gives us each of the attributes and their type, and here’s a list of all the records from that model. We can change what’s displaying, and then click through to get all the info for one. And using these exact same routes, we can do it for other models. And we can do it for any model with the exact same code. I’m going to show you how.

First, let me introduce you to the star of today’s episode, the attributes property. So the attribute property is used on a model class and it gives you the type and the name, as well as several other things about a specific attribute that is defined on a ds.model.

One thing to keep in mind is that this is a static property. So here we have an instance of a model, and if we try to use attributes on it, it’s undefined. But if we call for the constructor, which is the model class itself, and then use attributes, then it will give us something. And here it’s giving us a ComputedProperty, so it’s kind of difficult to explore in the console, but we’ll be exploring it in the code.

So first we have the route structure, which is first the route anything, and then a model which is just the model_name, and then record which is just the record_id.

Then we have the anything route, which it looks up the data-adapter on the container and gets all the model types. This is code that I stole from the ember-admin project, which does something similar to what we’re building, just more robust.

So then we take that list of models, we loop through them, and we link-to them based on their name. And you see here, we’ve got three in this particular project. We click it and it takes us to the monster index route, or the model index route, in this case monster.

Here we’re doing two different things. First, we’re pulling the same trick as we pulled in the anything route to get all the model types, but then we’re just finding the one that has that model name, monster in this case. And then we’re also using that model name to find all the records with that model name, and putting them into our return value.

Then what we’re doing with that is we’re giving the name of that model, and then we’re looping through each of these attributes. So we have the object, and we call .klass on it, and that gets us the klass, and then we can call .attributes. And then we’re listing them by name and by type.

That gets us this, here. Well actually it gets us these four. We put on id on the top separately. You will notice that although it does all the attributes, it’s not doing any of the relationships, so it’s not showing its relationship to team-membership. That is done with the relationships property, which we’ll go over it in a later screencast.

So that’s what that this ends up showing. And it should be noted that, remember we called .constructor earlier on an instance? This modelObject.klass gets us the same thing as the instance.constructor did. And so the .attributes we’re calling is the same as we called on the console earlier.

Alright, so that’s what’s up on the top. On the bottom here, in the index, what we’re showing is all of the records. So we’re looping through the records and then we’re linking to anything.model.record, with the record, and of course we’re showing the record.id down here. So we click through it, and it’ll show all the info.

And here it’s fairly straightforward. We’re first getting the params for the anything.model controller, and grabbing the model_name off of that, and so that’s the parent controls, the one we were just talking about.

And then we’re just calling a plain findRecord for that modelName, and using the record_id params that we pass in. And that will get us the model, which we then call .constructor.attributes on, and we loop through those, the attribute.name, and then we’re doing another bit of metaprogramming, we’re using the gitHelper to call attribute.name on the model. So it might be model.getImageUrl, or something like that. And that will get us this nice listing, like we saw before.

So that’s how you use the static attributes property in order to do some really cool metaprogramming.

Now two things before you go. First, you notice that we didn’t choose which field you display. We didn’t make that yet. I created a separate commit for that, it's just some query params. Second, there are a lot more properties besides just attributes. There’s static properties like fields, or relatedTypes, relationshipNames, relationships, relationshipsByName, and transformedAttributes. And not only that, there are methods that are related to those, such as eachAttribute, eachRelatedType, eachRelationship, and eachTransformedAttribute. So those are pretty useful, and we’ll go over some of the relationship ones when we expand this to do not just the attributes, but also the relationships, and we’ll do that in a later screencast.

I hope you’ve enjoyed this detour into metaprogramming as much as I have. I’ll see you next week.

Introduction to Ember Data 2.0

Subscribe to our mailing list