Series: Computed Properties

Computed Property Macros

Published on Jul 27, 2015

 

Break up giant functions and make your logic beautiful using Ember.computed macros.

In this episode, we learn the basics, then refactor a large computed property into smaller more readable macros.


Links

Code

canKickCan: Ember.computed.and('userHasAFoot', 'userCanSeeCan', 'copNotAProblem'),
user: Ember.computed.alias('currentlySignedInUser'),
userHasAFoot: Ember.computed.or('user.hasLeftFoot', 'user.hasRightFoot',
userCanSeeCan: Ember.computed.notEmpty('user.area.cans'),
copPayingAttention: Ember.computed.gt('cop.attention', 5),
copInUserArea: Ember.computed('cop.area', 'user.area', function(){
  return this.get('cop.area') == this.get('user.area');
}),
copAProblem: Ember.computed.and('copPayingAttention', 'copInUserArea'),
copNotAProblem: Ember.computed.not('copAProblem')

Transcript

If you’ve been using Ember for any length of time, you’re probably familiar with computed properties. Today we’re going to talk about computed property macros which is a way to use computed properties in a shorter and more concise way that gets your intent across clearer.

Let’s start with a quick review of regular computed properties. This is the canonical example. You have your firstName and your lastName, and you combine them to get the computed property of fullName. So you set the firstName and the lastName, and then it computes the fullName. Then if you change the firstName, then it recomputes the fullName. And then if you ask for the fullName again, then it doesn’t recompute it. It takes it from the cache. So it updates automatically when one of its constituent properties is updated, and it caches that result to make your app run much faster. So that’s what we’ll probably review.

Let’s look at an example that shows the need for computed macros. Here we have an example from our dystopic Sci-Fi world where kicking a can is illegal and it’s something that people do as an act of rebellion. From our comments we can see that if the currently signed-in user, they can kick the can if they have one of their feet, if they are in the same area as a can, and if the cop is not a problem. So we have to have that comment because the function itself is a little messy, and that’s a problem because here we’re going to, in a slide, change just one thing, and suddenly the comment is wrong. It says they can kick the can if they have one of their feet, but now the function says that they have to have both of their feet, and it’ll take a very careful reading to see that the comment is incorrect.

So we’re going to be using Ember.computed and computed macros to try to make it so it’s easier to understand, it’s easier to edit, so things are shorter, and so that no comments are necessary. Once again, the goal is no comments for individual functions. We want the function names to be so descriptive and the functions themselves to be so concise that it’s very easy to tell what they do. How are we going to do this? We’re going to break it into logical sub-properties, and then we’re going to use the Ember.computed macros to make it beautiful, to make it more readable and easier to edit.

To start off with, let’s reduce this down, this part of the can function to a function that is better named. To start with, we’ll have the user be an alias for currentlySignedInUser. So we have the function, it just returns currentlySignedInUser. And then userHasAFoot will take user.hasLeftFoot and user.hasRightFoot and combine them and return true if either one of them is true. And it takes that logic and it gives it a good name. And then canKickCan can use that logic that has a good name, so it’s much more readable. userHasAFoot is way more readable than this.get('currentlySignedInUser.hasLeftFoot') or this.get('currentlySignedInUser.hasRightFoot'). And the great thing about naming it this descriptively is you can go back and check to see if the logic is correct. So let’s see some more functions.

userCanSeeCan, we can see that this means that the number of cans in the area is greater than 0. copPayingAttention, the cop’s attention is greater than 5. Cop in area is if the area of the cop is the same as the user area, and then copNotAProblem is a combination of copPayingAttention and copInUserArea. So we’re building stuff up from the building blocks, and with those building blocks canKickCan reduces down to just userHasAFoot and userCanSeeCan and copNotAProblem. Notice that this is the same function. Just because we’ve been having computed properties that we can build on, it’s much more readable. In fact, it’s more readable than the comment that we had before, and we can look at it and we can look at all the functions and see that they do what they say. So it’s much easier to verify if it’s correct.

So that’s a lot of good parts, but along the way we created a lot of boilerplate. There’s a lot of Ember.computed. There’s a lot of listing the properties that are part of it and then using them. So we’re repeating a lot of information. And that’s when Ember.computed macros come to the rescue. So you can see it’s a lot shorter. There’s a lot less fewer characters than before, and the things we’ve eliminated were repetition boilerplate. Let’s look at each one of these individually.

So with canKickCan we’re using Ember.computed.and, and and it just puts an ampersand, double ampersand, in between each of the properties that it takes. And it can take as many properties as you want. Here we’re giving it three. You can give it two, you can give it eight, as many as you want. alias is just saying okay, take this and we’re going to call it this. It’s the same as just returning a property. Here we have or which is like and except for the or functionality. We have the notEmpty functionality. So the user.area.cans , if there’s something there then notEmpty will return true. There’s also the empty functionality which would return true if there was nothing there. As you can see previously, we’ve been doing that by calling .length and checking if it’s greater than 0. We’re also going to use the gt (greater than) macro which checks to see if the cop’s attention is greater than 5. We also have available the less than, lt macro, the greater than or equal gte macro, and the lte less than or equal macro.

Here we’re going to try to use the .equal, but unfortunately, this is not actually how it works. How it works is it has the first argument is dynamic and the second argument is static. So here, we’re checking that our hamster, its state is sleepy. If we set the state to sleepy, napTime is true, if we set the state to hungry, napTime is false. And here we were trying to have both of them dynamic which is not what it does, so we’re going to leave that one alone.

We can’t do everything with computed macros, but back to computed macros. Here we’re taking one thing and turning it into two, because we don’t have an and macro but we do have and and and a not. And so we’ll combine the and with copPayingAttention and copInUser, and a not for copAProblem. So it all leads to this, this much cleaner thing where almost everything is one line, and you can tell what each descriptive thing does and check very easily whether it’s doing the correct thing. There are no comments needed, and it’s actually easier to read than something with comments.

So in this video, we’ve broken our giant computed property into logical sub-properties, and then used Ember.computed macros to make it more beautiful and easier to read. In practice you would do that, you would break into logical sub-properties and you’d use the Ember.computed macros while you’re doing that. You wouldn’t separate it out into two steps.

So there’s more documentation here, and there’s also more macros, and I’ll be going over those in later screencasts. I hope you find these useful. I’ve found them incredibly useful in my day-to-day code. Good luck.

Computed Properties

Subscribe to our mailing list