TL;DR
This is a gauge (well kind of) made with Ember Components and CSS3 transforms. The pointer
angle is bound to those 2 inputs (see the jsbin just bellow) in which you can define either the pointer
current value and the whole gauge maximum value.
The Template
The main idea of having a gauge component is that you can use and re-use it anywhere in your Ember app(s), and its main purpose is to indicate a current value relative to a maximum value. So, if it could have 2 attributes value
and maxValue
that would be perfect.
Well, let’s just do that.
{{my-gauge value=something maxValue=something}}
And here’s how it would look like under the hood.
<script type="text/x-handlebars" id="components/my-gauge"> <div class="frame"> <span class="pointer"> <span class="pointer-cap"></span> </span> <span class="scale"></span> </div> </script>
Note: for the purpose of this post the template is inside these
<script type="text/x-handlebars">
tags, but you could set your own template precompiling tools quite easily (grunt-ember-templates is pretty good at this) and you would have amy-gauge.hbs
file inside acomponents/
folder.
The most important part here is the computedAngle
property which is bound to the style
attribute of the pointer
element. In other words, it holds the correct CSS rules to apply the right angle (CSS transform – rotate()
) to the gauge pointer
.
If you’re curious about the {{bind-attr}}
part, you should take a look at the Guides in Ember documentation, because there are many different ways of binding data to / from templates.
The MyGauge Object
The template now needs a little bit of magic logic, let’s extend the Ember.Component
object so we can define our gauge properties.
App.MyGaugeComponent = Em.Component.extend({ // Em = Ember classNames: ['gauge'], computedAngle: function(){ // parseInt because we want Numbers and inputs value actually are Strings var value = parseInt(this.get('value'), 10); var maxValue = parseInt(this.get('maxValue'), 10); var angle = Math.floor( 180 * value/maxValue - 90 ); var styles = 'transform: rotate('+angle+'deg)'; return styles; }.property('maxValue', 'currentValue') });
Note: For the sake of brevity, I intentionally omitted the CSS vendor prefixes in the
styles
string.
The computedAngle
function returns a styles
which is a string containing the CSS rules that will be applied to the .pointer
element.
You probably noticed the .property('')
at the end of the function, this tells Ember to consider that function as a property so it can re-execute itself if the value of other properties (passed as argument) changes. In other words, if either value
or maxValue
changes, then computedAngle
is re-executed.
Oh God! How do you rotate that stupid needle!?

I feel a bit like this is the trickiest part of the component because actually CSS is doing like 90% of the job. I do love CSS, but sometimes it’s just… omg. So, yes sorry for that 2Mb GIF I couldn’t resist 🙂
Basically the pointer
rotation is made with CSS3 Transforms, the computedAngle
property updates the angle
and CSS3 Transitions handle the animation when angle
changes.
But the most important CSS property is the transform-origin: bottom
, because it applies the rotation from the bottom of the pointer
instead of from the center (the default). By the way, this is how those crazy CSS clocks are made.
Here’s a little diagram, thought it might help.

Ok, we get the correct angle with this formula: 180 * (value / maxValue)
. BUT, the pointer has to be set to 0° by default, and if you pay attention to figure n°3 in the diagram above, then you’ll see that 0° is in fact -90°. So let’s subtract those 90° from the formula: 180 * (value / maxValue) -90
.
And now we can create a string: var styles = 'transform: rotate('+angle+'deg);'
, which will be injected into the pointer
style
attribute.
Of course, vendor prefixes will mess a little bit the final string, but it’s not really a big deal.
Detect if maxValue is exceeded
Now let’s see how we can detect when value
exceeds maxValue
, we’ll simply use another computed property for that.
App.MyGaugeComponent = Em.Component.extend({ classNames: ['gauge'], classNameBindings: ['isMaxValueExceeded:exceeded'], isMaxValueExceeded: function(){ // parseInt because we want Numbers and inputs value actually are Strings var value = parseInt(this.get('value'), 10); var maxValue = parseInt(this.get('maxValue'), 10); return (value > maxValue); // return a Boolean }.property('value', 'maxValue'), … …
It’s just a test to see if value
is strictly greater than maxValue
, and again isMaxValueExceeded
is watching for any change of value
or maxValue
with the property()
method.
And now there’s this classNameBindings
property which is used to add or remove a class name on the component element if a property is either true or false. Here it adds an .exceeded
class if isMaxValueExceeded
is truthy. The syntax for this is a little bit like ternary operators, so a real life example could be isVisible:shown:hidden
which adds a shown
class if isVisible
is true and a hidden
class if isVisible
is false.
Also, you could use isMaxValueExceeded
inside the template with an {{if}}
statements, so for instance we can print a warning to the user.
{{#if isMaxValueExceeded}}
<p>OMG maxValue is exceeded!</p>;
{{/if}}
Conclusion
Ember Components give an incredible amount of power to build reusable blocks that you can share across applications, and since it mimics W3C’s Web Components as much as possible, it feels a little like tasting a bit of the future.
And yes of course, all of this works well for half-circle gauges. The formula to update the pointer
angle, position, height or whatever, will slightly differ if you want another type of gauge, but I hope you got the main idea.
Resources
- A bunch of components, helpers, views, on Eric Berry’s website
- Ember Components Guide in the documentation
- A tutorial on how to build a custom select box Ember Component
- A good introduction to Ember Components by Ryan Anklam
- Twitter Bootstrap 3 “Ember Componentised” – github | demo
The CSS from the JS bin is not there anymore. It’s just “afasf”
Fixed. Weird indeed…
Thanks! This is an awesome example of using Ember Components.
Ahah thanks! 🙂
Beautiful!
Nice demo. Bug: No lower bound on value. Try -10000 and watch the dial spin!
Yeah I know. Isn’t it fun? 😀
thanks for the demo and explanation!
Thank you guys for your support. I really really appreciate 🙂
What is the license for this component?
ah yeah … it’s a WTFPL.
Awesome
Very good article. I absolutely love this website. Keep writing!
Great work!
Here is my Coffeescript version