Skip to content

Instantly share code, notes, and snippets.

Last active March 30, 2017 05:37
Show Gist options
  • Save polotek/928a1ae8bbf401a11b8b to your computer and use it in GitHub Desktop.
Save polotek/928a1ae8bbf401a11b8b to your computer and use it in GitHub Desktop.
Hurdles getting started with Ember.js

This is a brain dump of my experience trying to get something going with Ember.js. My goal was to get to know the ins and outs of the framework by completing a pretty well defined task that I had lots of domain knowledge about. In this case reproducing a simple Yammer feed. As of this time, I have not been able to complete that task. So this is a subjective rundown of the things I think make it difficult to get a handle on Ember. NOTE: My comments are addressing the Ember team and giving suggestions on what they could do to improve the situation.

App setup

The new guides have pretty good explanation of the various parts of the framework; routers, models, templates, views. But it's not clear how they all get strapped together to make something that works. There are snippets of examples all over the place like: {

But they're out of context. What is App.Router? Is it an instance of a Ember.Router or a subclass? In trying to set up my App, I got tripped up for hours on where it was appropriate to do extend() or create(). I thought I had an understanding of the difference between these (though I don't think you can count on that from all your users). But I found the behavior of these in Ember thoroughly confusing. I'll come back to that later. Suffice it to say it wasn't clear to me which one the framework required at any given time.

So App needs to be an instance, Ember.Application.create(). But then you need to set properties on that instance directly, App.ApplicationController = Ember.ObjectController.extend();. And that needs to use extend(). And you can't pass in an object to set the properties of the App instance.

// Doesn't work, even though it's in some of the docs
var App = Ember.Application.create({
  ApplicationController: Ember.ObjectController.extend()

I lost a lot of time trying to figure that out.

You need to talk about how to set up an Ember Application and what is actually happening. Here are some rough things I pieced together. You can see how I did based on how wrong and incomplete they are.

  • You must create an instance of an application.
    • It functions as a namespace, so if you don't title case it (app vs. App), you'll get a weird warning. It won't stop you from continuing, but the warning is disconcerting and unhelpful.
  • You must create Classes and assign to properties on the App instance
    • You need to use extend() on pretty much all of these.
    • I believe these will be "registered" with the App automatically. If they are not placed on the App object, things don't work. It's not clear if you can manually register Classes with the App.
    • Near as I can tell, Ember will look these up by naming convention and create instances of them at "the right time". It's not clear when that is or if the instances get reused.
  • The App will initialize and auto-start after an asynchronous turn. Some docs have an explicit call to App.initialize(), that doesn't seem to be necessary anymore. Definitely unexpected. Not sure if auto-start can be disabled.
  • You MUST create App.ApplicationController, App.ApplicationView, and App.Router. If you don't, you get more unhelpful errors.

Even with all of these things, I'm not sure how to get an Application that does nothing and doesn't error. It's difficult to start from a working no-op App, and then add functionality incrementally so you can learn.

Creating classes and instances

The Object Model section of the guide talks about Class and Instances. Classes are created using extend() and instances are created using create(). This seems fine, but once you get these objects, the javascript rules we've come to expect start breaking down.

var O = Ember.Object.extend({
  classProp: 'classProp'
  , classMethod: function classMethod() {}

// good
typeof O // "function"
O.prototype.constructor === O // true

// WAT
O.toString() // "(unknown mixin)".
             // Also displays this in the chrome console instead of
             // letting you inspect the Object or printing the
             // function body.
O.prototype.classProp // undefined
O.prototype.classMethod // undefined

var o = O.create({
  prop: "prop"
  , method: function method() {}

// this seems to behave normally
o instanceof O // true
o.prop // "prop"
typeof o.method // "function"
o.classProp // "classProp"
o.__proto__.classProp // "classProp"

// Except when you evaluate `o` in the Chrome console,
// it prints an object named "Class"

// WAT. now the constructor somehow behaves too
O.prototype.classProp // "classProp"

I can understand doing some trickery with your type system if it adds value. But you don't explain what that value is. And this weirdness is another thing that makes it hard to figure out what's going on by inspecting objects.

Router API

I have no idea how the router is supposed to work. Early on I was using the old router api and I understand you guys aren't proud of that one. But I didn't find any good tutorials on the api. Even others who are publishing working examples say in their docs that they don't really understand the magic encantation for the router. They just put it in and things worked.

It seems intuitive that connectOutlets allows you to fill the places that say {outlet} in your template. But the api for this method is thoroughly confusing. I kept finding slightly different examples around the web. Probably due to evolving apis. But some questions that still stick out:

  • What is the object context in this method? What does this refer to?
  • The first argument seems to be router. Is that an instance of router or the same as App.Router? Is it the same instance for the whole app?
  • Are there other arguments? If so, when are they passed and what's their significance?
  • router.get('applicationController'). How exactly do I know I can do this? What's the convention for what you can get out of the router?
  • applicationController.connectOutlet. Again, what's the method signature here? It seems to take the name of a template (or is it the name of a View?). The other argument seems to be a model or a controller? Still confused about what's happening there.

After a while of things kind of working, but being frustrated, I was fine discarding that for the new API. So I'm currently running master so I can use the new Router. I've read and watched videos about it. But it just spits errors for me. And there doesn't seem to be enough documentation on it yet to figure out what's going on. Would love to drop back to the old router which at least let me continue without really understanding. But that doesn't seem to be an option. I'm all about progress, but it feels like the api is currently in a gap of uncertainty between old Router and new.

Ember data

I've been intrigued by ember data since first learning about it. It's an attempt to create an abstract data layer that can represent models and relationships on the client. Essentially allowing to create a sensible data domain for client-side apps. We have a custom solution for this at Yammer, and it was really interesting drawing parallels. That said, I had a really hard time doing that and never actually got things working.

My exploratory project was a little app to display a simplified Yammer feed using test yammer data. We have custom api urls that don't follow rails conventions, and a custom data payload format. I was interested in attempting a really simple custom adapter/serializer combo that could load our data. I read lots about adapters and serializers and looked at your youtube videos explaining them. I even dove into the code and read the extensive comments there. But when it comes down to it, the architecture is just too opaque. There are so many open questions to answer in order to go from a call like app.Feed.find(), to an ajax call with the correct url and data, to a valid Model that will load data successfully. Here are a few:

When filling in YamAdapter#find, what's the significance of this stuff that I pulled from the built-in adapters?, function(){
  this.didFindRecord(store, type, json);

I know about the Ember run loop, and I'm assuming didFindRecord passes things off to the serializer among other things. But there's a lot to put together here.

The adapter methods don't return anything. I vaguely understand that they get called via the Store. But when you consider that the Model methods return those promise-like records, it's very unclear how these adapter methods connect back to those.

The adapter uses methods like rootForType() and buildURL() to figure out what ajax calls to make. I found that sometimes I didn't have enough context to build the url I needed. The only data that gets passed in for find() is a type and an id. If you need to put more parameters onto the url, the adapter has to get those from somewhere. I ended up adding a 4th data argument, but that means I'm firmly outside of the realm of compatibility.

Once I made it into the serializer, things got even more hairy. I read a lot about extracting vs. materialization. But I couldn't figure out the right combination of things to create my models and wire up the relationships properly. It looks like the core of extraction is the method loadValue which creates a Model instance in the store. I got that far, but then I don't really know how to hook up relationshiops. I see things like "prematerialized" and I'm not sure how that factors in. My promise-models never loaded and I got bogged down in the weeds.

I tried to take a step back. Because of my background with the Yammer paradigm, I understand what's supposed to be happening at a high level. I want to end up with a set of models created from the data payload and have their relationships hooked up. Nevermind the events that need to be fired for now. I tried to figure out how to just process the payload manually because I know exactly what's in it. But I don't know the right methods to do so. I'm thinking of something like the following:

var messages = {
  var message = App.Message.createRecord(msg)
    , thread
    , user;
  // look up references
  _.each(json.references, function(ref) {
    switch(ref.type) {
    case 'thread':
      if( == message.thread_id) {
        thread = ref;
      } else { return; }
    case 'user':
      if( == message.sender_id) {
        user = ref;
      } else { return; }
  if(thread) {
    // set the thread relationship on the message
    thread = App.Thread.createRecord(thread);
    message.setRelationship('thread', thread);

  if(user) {
    // set the user relationship on the message
    user = App.Thread.createRecord(user);
    message.setRelationship('sender', user);

All this is just to do a simple find(). There were a few other things I wanted to get going. I still don't exactly get how sinceQuery or extractMeta fit in. I think I get it conceptually, but I don't think it's flexible enough to fit my needs. There's a gap between looking at the RESTadapter/RESTSerializer, which are geared towards rails conventions, and looking at the base adapter/serializer which are pretty low level and don't give you much help in filling in gaps.

I still think ember data is an impressive undertaking. In it's current state, it's gonna be pretty difficult to take advantage of if you have non-trivial api requirements.


Where are the tests? I know they exists. There's a tests folder, but there aren't any readable tests there. Things are obfuscated, probably for the test harness. But this is a mistake. When trying to learn a system, tests are a great resource to see examples of usage and get a better understanding of components. But that avenue isn't available here. It also hampers contributions from folks who want to help improve things.

Also I can't run the tests without phantomjs? That's no good. Ember is for the browser first and foremost right? IMO, tests for browser javascript should always be available by just hitting a url. Chrome dev tools is the preferred tool for debugging. You can add options for running in a headless browser or node. But browser should be the default.

I know there are no good standards here yet. But I think you get a lot of mileage out of these suggestions.


My foray into Ember land was extremely enlightening. Make no mistake, I could write a document this size about the stuff I like as well. But the barriers to entry are really significant unless you have a rails background. It's okay to lean on familiarity with rails if that's the audience you want. But if you want to capture more of the larger js community, you've gotta pull back the covers a bit more. Or at least you need to give us a thorough introduction to Convention over Configuration in the context of Ember, because not all of us are going to learn rails just to learn Ember.

I hope this has been helpful, and I'd be happy to talk to you in more depth about any of it. I should be clear that this exercise was for my own education, and I was not evaluating Ember for use at Yammer. Yammer's just a great example for getting a non-trivial overview of what frameworks can do.

Copy link

@toranb thanks for your screencast on the new router, I'm 30 minutes in and can already see the forest for the trees :D

@sly7-7 Thanks!

Copy link

I like Ember and want to prefer it over other frameworks..But there are so many changes happened and expecting more, its really making difficult for the people like me....

Copy link

Thanks for putting all your thoughts together and share.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment