Ember.js and Related Technologies
Another common point of confusion for Ember newcomers is naming. Not only does proper naming help keep your application code cleaner, it also has an effect on your application's operation. Once you get the hang of it, it's quite simple so read on to learn more.
The Ember rules for capitalization are pretty straight-forward. If it's
a class or a namespace, it's uppercase; if it's an instance, it's
lowercase. Namespaces are the only objects that should be declared as
globals, i.e. classes and instances should always be declared on a
namespace. Some examples: Ember and your application are namespaces
1, Ember.Object and Ember.View
are classes, and MyApp.personController and myObj are instances.
The first reason for this naming convention is that it makes it quite simple to figure out what kind of object you're dealing with. If it's lowercase, it's an instance, if it's uppercase, it's a class or namespace.
Secondly, and more important, because of this convention, Ember can make some assumptions about your code and behave more intelligently as a result.
Lets work with the following code:
MyApp.controllerOne = Ember.Object.create({
value: 'one'
});
MyApp.controllerTwo = Ember.Object.create({
Constant: 'const',
valueHash: {
value: 'two'
},
valueBinding: 'valueHash.value',
valueOneBinding: 'MyApp.controllerOne.value',
constBinding: 'Constant'
});
// Sync our bindings. Not necessary in normal use.
Ember.run.sync();
MyApp.controllerTwo.get('value'); // two
MyApp.controllerTwo.get('valueOne'); // one
MyApp.controllerTwo.get('const'); // undefined
So, if you've got a basic understanding of Ember, you'll understand why
value and valueOne return their respective values. However, you
might be surprised to see that const returns undefined. The
explanation is simple: Ember sees the capital and assumes that this must
be a namespace and looks for the global Constant, which, of course, is
undefined.
So what do we do if we actually want to get the local property? All we
need to do is prepend this. to our binding, as such:
constBinding: 'this.Constant'
This tells Ember that we're looking for it on the current object instead of the global level.
Consider the following javascript:
window.myProp = 'prop';
MyApp.controllerOne = Ember.Object.create({
value: 'one',
Constant: 'const'
});
MyApp.controllerTwo = Ember.Object.create({
value: 'two',
});
and template:
{{#with MyApp.controllerOne}}
{{value}}
{{Constant}}
{{MyApp.controllerTwo.value}}
{{myProp}}
{{/with}}
This outputs the following:
one const two
If you're looking carefully, you may have noticed that our behavior here
varies a bit from bindings, so let me explain. When Ember encounters a
bound property in a template it checks to see if that property exists in
the current context, MyApp.controllerOne in this case. If the property
is not defined, then Ember checks to see if it begins with an uppercase
letter. If it does, then we assume it is a namespace and look for it
globally. This is what happens in the case of
MyApp.controllerTwo.value. Ember will not look for lowercase property
names globally, i.e. myProp.
So why don't bindings check both locations like we do in templates? The
main reason for this is performance. Bindings use get and getPath
internally. These are both very hot code paths and having to continually
check two locations adds up over time. Templates are rendered with much
less frequency so a bit of extra work there doesn't end up being a
problem.
Another useful trick that comes into play is creating your own
namespaces. As we mentioned earlier, your app is actually a namespace as
well. Ember.Application is a subclass of Ember.Namespace. You might
be wondering why we need a custom namespace class. Why can't we just do
MyNamespace = {}? The main benefit of Ember.Namespace comes when you
attempt to inspect your objects. Let me show you:
NamespaceOne = {};
NamespaceOne.Object = Ember.Object.extend();
NamespaceTwo = Ember.Namespace.create();
NamespaceTwo.Object = Ember.Object.extend();
NamespaceOne.Object.toString(); // (subclass of Ember.Object)
NamespaceTwo.Object.toString(); // NamespaceTwo.Object
As you can see, when we use Ember.Namespace, lets toString provide us
with more information about the object. We're able to do this with a bit of
creative code that actually scans window for instances of
Ember.Namespace and then, in turn, examines the properties of each.
If that hasn't all sunk in well, you can basically remember the following:
this..Ember.Namespace for creating custom namespaces.1. Yes, technically your application is itself an instance, but since it functions as a namespace we capitalize it as well. We'll talk about this more later.