Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Ember Tutorial (vicramon.com)
127 points by hpvic03 on June 12, 2014 | hide | past | favorite | 62 comments


The more I look at Ember the more it reminds of a framework named Batman[0] I used over two years ago. It's almost a complete replica, all the way down to Ember.Object being identical to Batman.Observable.

The problem with Batman (aside from being buggy) was that it tried to do this same MVC that we use on the server on the client, and the mapping just doesn't make sense. HTTP "MVC" doesn't have state between requests whereas client-side MVC has state for the entirety of the app. Rails, for example, has a Router and Controller due to the synchronous nature of how a request comes in and gets turned into a response. The first place I see MVC JS frameworks fall apart is when they have both Controllers and Routers. Ember Controllers look like little more than Decorators. I imagine these would be called a ViewModel if named appropriately.

Over two years later, I can't help but feel Backbone (minus the underabstracted View layer, which can be easily replaced with React) got the mental model for JavaScript apps right from the start.

Ember just seems to have been marketed much better than Batman was, which is no surprise, Yehuda is good at doing that.

0 - http://batmanjs.org


> Ember just seems to have been marketed much better than Batman was, which is no surprise, Yehuda is good at doing that.

I don't think this is fair. We use Ember at work, although I wish we used React. We started out on Backbone and it was miserable. Ember has gotten popular because it and Angular were seen as the successors to Backbone for a long time. Ember is designed in a much more thoughtful (and I think better) way than Angular.

Yehuda has done a lot of good work on Rails, Bundler, jQuery. That probably helps sell people on his projects. I wouldn't chalk it up to marketing.


> We use Ember at work, although I wish we used React.

Could you expand on why you prefer React? I have not tried React yet, but am interested to hear thoughts from someone who's used both.


I'm interested in that too. Is it fair to compare Ember and React though? It seems like they are trying to solve different issues.


Think of Angular directives, Ember components, and React components as attempting to solve the problem of web components in your view.

I'm using react right now in a rails application at work. While it hasn't totally replaced my haml views, I definitely sprinkle it in where appropriate. It does a great job of handling dynamic uis and gives a standard, easy to understand structure to my javascript. I think of it is as a next step beyond jQuery.

In my opinion, React forces you to think of your view as a hierarchy. You are forced to think about what components make up your view and what its responsibilities should be. React components also come with some standard methods in regards to the lifecycle of each components and the state of your data as it flows up and down the hierarchy of your components.

I've really enjoyed the experience thus far. I'm not necessarily a fan of putting any sort of html-like structure in my javascripts, but Pete Hunt, one of the core contributors to React, explains his reasonings quite well in many talks you can find on Youtube.

--

Also, Khan Academy uses Facebook React in its perseus q&a system. You can view blog posts by two Khan developers about React here:

http://benalpert.com/2013/06/09/using-react-to-speed-up-khan...

http://joelburget.com/backbone-to-react/


Indeed but Ember components are only a part of the Ember framework.

What you describe in the rest of your comment is equally applicable to Ember (if not more, since Ember goes beyond the view layer).


You're right. Yehuda's great work and open source contributions definitely help a lot. I don't want to discount that.


> The first place I see MVC JS frameworks fall apart is when they have both Controllers and Routers.

You have to remember that MVC is a very, very loosely defined term. The Ember MVC is very, very different than the Rails MVC. Ember's is much closer to Cocoa or Smalltalk in this regard.

To see how these bits fit together in Ember, I highly recommend seeing Tom's talk on URLs and their importance: http://vimeo.com/77760308 (you can skip the first 12:35 or so, and around 30:38 is where it really gets into it.)


Oh, ThoughtBot wrote a WONDERFUL post about this today: http://robots.thoughtbot.com/shared-terminology-yet-differen...


Exactly. Ember is directly inspired by Cocoa, and Cocoa has been using stateful MVC with incredible success for the past decade.


The reason why Backbone views are so minimal is precisely so that you can use the HTML/UI abstraction of your choice.

For some teams that's Underscore templates, for some it's Handlebars, for some it's in-house widget libraries, for some it's React.


But the UI abstraction is the hardest part of JS, which is why so many people are moving away from Backbone, it doesn't help you with it at all.


> "(...) got the mental model for JavaScript apps right from the start."

If you remove the Views from Backbone the only thing left of value are the Models and Collections. Are you saying they got those "right"? I really have no idea what that means. On the contrary — I think Backbone got a number of things wrong. Their router especially.

> "Ember just seems to have been marketed much better than Batman was (...)"

You said that Batman was buggy and got MVC wrong. Perhaps Ember is /not/ buggy and actually manages to deal with state quite well?


Ember handles App state very well. It is easy to take advantage of local storage when you need it, and the way that changes in the data are bound with changes in the view makes it very easy to create pro-level web applications.


Nothing about what you just said is specific to Ember. Two-way data-binding is common in just about every framework, or can be added in cases like Backbone.

Easily using localStorage is also common because just about every JS framework uses the Adapter pattern for persistence.


I've been working with Ember over the past few months and am still impressed with how simple and productive it makes every day tasks. Basic CRUD stuff, especially render code, is a lot less drudge work. I'm even more excited for where the community is leading the framework, through projects like ember-cli and htmlbars.

Ember is conceptually pretty massive though. A tutorial like Michael Hartl's Rails tutorial would be a huge benefit, it looks like that's what this is aiming for. Thanks!


On the other hand, I've found many things lacking in Ember.js, though most of my gripes are with ember-data, so I don't know if you're thinking of that as part of Ember.js.

A couple of examples of Ember.js-specific gripes though:

- We have a table with one of the filters just stops working after changing the value 4 ~ 5 times. I've dug into this, but I can't really figure it out without going into the Ember.js internals (which I have done before, but the time sink isn't currently worth it). I've boiled it down to the fact that at some point Ember.js stops responding to changes on a particular attribute. Computed properties stop working sooner, but even a .observes('attribute') stops triggering after 5 or 6 changes.

I get that software is not bug-free, but how am I supposed to even debug something like that? It would be fairly difficult (and time-consuming) to boil it down to a simple test case, as this is the only place we're seeing this happen and it's in a large Ember.js application.

- There is no case/switch statement in Handlebars. I'm left with deeply nested if/unless blocks, or tons of computed properties on controllers/views that generate this stuff. E.g.:

  {{#if valueLoading}}
    Loading...
  {{else}}
    {{#if valueLoadingFailed}}
      Error!
    {{else}}
      {{#if value.length}}
        <ul>
        {{#each item in value}}
          <li> {{item}} </li>
        {{/each}}
        </ul>
      {{else}}
        No Items.
      {{/if}}
    {{/if}}
  {{/if}}
vs.

  {{#if value.length}}
    <ul>
    {{#each item in value}}
      <li> {{item}} </li>
    {{/each}}
    </ul>
  {{else}}
    {{stateMessage}}
  {{/if}}

  ... and on the controller ...

  stateMessage: function () {
    return this.get('valueLoading') ? 'Loading...'
         : this.get('valueLoadingFailed') ? 'Error!'
         : this.get('value.length') === 0 ? 'No Items.'
         : '';
  }.property('valueLoading', 'valueLoadingFailed', 'value.length'),


1. Are you using Ember.computed's array methods? I tend to go with intermediately calculated arrays which I then union/diff/whatever is necessary for filters. It is also significantly faster than function-defined computed properties. The only bug I know of for this functionality was fixed in the 1.5 branch by @hjdivad.

2. The typical pattern is lots of computed properties. They're lazily calculated, so that makes them pretty cheap.


> 1. Are you using Ember.computed's array methods? I tend to go with intermediately calculated arrays which I then union/diff/whatever is necessary for filters. It is also significantly faster than function-defined computed properties. The only bug I know of for this functionality was fixed in the 1.5 branch by @hjdivad.

This was a single value (e.g. 'selectedItem') populated by a Ember.Select view/component. After changing the selection a number of times, all things watching 'selectedItem' completely stop firing off (until a page refresh). No idea why.


There is going to be some future work on some of the form elements. They're really not quite where they need to be.


I hope it works out for you but that's just about exactly how I felt immediately before I realized it was not going to work for a very large project, switched to Angular and haven't looked back since. There was no comparison at all for me between Ember and Angular but everyone's needs are different.


What's weird is that I constantly hear the opposite. Angular's free flowing structure allows for spaghetti code in scale, while Ember might be overkill for smaller projects.


Ember's examples on their site are always the simplest use-case. For example, find me an example on their site of saving a complex relationship that was newly created by your application. Let's take the example of an invoice. Let's say you have an Invoice model, and an InvoiceLineItem model in a one-to-many relationship.

How do I save the creation of a new invoice (with line items) as a single transaction? As far as I can tell, you can't without breaking from Ember Data's very strict 'this is the way things should be done' model.

You have to create a new Invoice, then create the InvoiceLineItems doing something like this:

  var self = this;
  self.get('store').createRecord('invoice', {
    .. attributes ..
  }).save().then(function (invoice) {
    return Ember.RSVP.all(self.get('lineItems').map(function (item) {
       return self.get('store').createRecord('invoiceLineItem', {
           invoice: invoice,
           .. attributes ..
       }).save();
    });
  }).then(function (invoiceLineItems) {
    // do something
  }).then(null, function (error) {
    console.error(error);
  });
Note that this isn't even taking into account how to rollback INSERTs if, for instance a single invoiceLineItem fails to be created.

The 'other' way to do it is to create just an Invoice model, and have a lineItems attribute of type 'raw' and just create a RawTransform that's just as pass-through of whatever the JSON has for that attribute. It works, but at the same time, feels like it's going against the Ember Data Way, especially if you have a need for an InvoiceLineItem to be its own model (i.e. to be able to maintain relationships to other things and look one up by ID via the API).

Then you might have something like:

- Invoice and InvoiceLineItem models

- A RawTransform:

  App.RawTransform = DS.Transform.extend({
      deserialize: function (data) { return data; },
      serialize:   function (data) { return data; },
  });
- An attribute like so (on Invoice model):

  lineItems: DS.attr('raw'),
- Code that looks like this:

  var self = this;
  self.get('store').find('invoice', invoiceId).then(function (invoice) {
    self.get('store').pushPayload('invoiceLineItem', { invoiceLineItems: invoice.get('lineItems') });
  });


I really wish there was some thorough examples of using Ember + Ember-Data. Ideally it would be backend agnostic (mocked client side xmlhttprequest) and go from doing simple things like saving a model all the way to doing more complex things like you showed.

Drupal for instance has a /really/ great project that maintains a bunch of thorough examples (https://drupal.org/project/examples) which were indispensable when I did Drupal a while back.


It's kind of funny, but I don't even remember simple things being spelled out. For example, in their use cases all of the models and routes are single words like: post, comment, todo, etc.

In some places, you see 'Post' and others 'post', but there is nothing explicitly spelling out how that works for multi-word items (e.g. MultiWord => multiWord). Same with underscores in route names (e.g. route name 'multi_word.item.index' => MultiWordItemIndexRoute).


Yeah, the magic rejiggering of names everywhere with no clear documentation of what mapped to what† killed my interest in Ember the first time I tried to actually use it for even a fairly simple app.

† Plus the enforced snakeCase/CamelCase everywhere, but that's more to do with me far preferring names_with_underscores in general.



That's a good generic overview but doesn't hit how the code handles the edge-cases.


This is one of the reasons that Ember.js + Rails seems like the preferred setup. Ember.js + Python gets you into converting underscored names to camelcase names.


Maybe to clear some confussion, snake case is exactly naming_things_with_underscores.


And SCREAMING_SNAKE_CASE, one of my favourite names for a convention ever, is when snake case is in all caps ;)


Replying to the sibling. There was documentation of how the names changed - you just need to know where to look:

https://github.com/emberjs/data/blob/master/TRANSITION.md


I'm not talking about transitioning from 'old' Ember Data to 'new' Ember Data. I'm talking about someone completely new to Ember looking through the examples and documentation. Even if they were looking through the repository, it would be easy to overload a file called "TRANSITION.md" if you don't need to transition from an older Ember Data version.


There really should be better examples. In the mean time, you should check out the Balanced Dashboard, I've learned a good amount from it

https://github.com/balanced/balanced-dashboard


> Ember's examples on their site are always the simplest use-case.

This is really key. Even extremely common patterns that will be present in virtually every web app have no examples that I can find anywhere on the web. A great example is simple nested resources, where the URL is something like `/tv_shows/555/seasons/3/episodes/2`. Obviously, this is the page that displays info about episode 2 of season 3 of the TV show with id 555. So how do I reference the tv_show model (which I will almost certainly need to do) from the `EpisodesIndexController` or the `episodes/index` template? Do I really have to specify `needs` [0] on every controller nested under `TvShowsController`, and perhaps add a `setupController` hook to make the property easily accessible like in this tutorial [1]?

Granted, I'm pretty new to this, so maybe I'm just missing the obvious way to do this sort of thing clearly.

[0] http://emberjs.com/guides/controllers/dependencies-between-c...

[1] http://balinterdi.com/2014/02/26/a-common-resource-route-pat...


> Even extremely common patterns that will be present in virtually every web app have no examples that I can find anywhere on the web. A great example is simple nested resources, where the URL is something like `/tv_shows/555/seasons/3/episodes/2`.

http://emberjs.com/guides/routing/defining-your-routes/#toc_... ?

See also http://hashrocket.com/blog/posts/ember-routing-the-when-and-... and http://ember101.com/videos/007-nested-routes/

These are the first three results for "Ember nested routes" on Google. They're all pretty simple and straightforward. Hope that helps!


Perhaps my wording wasn't great, but I'm talking about the specific issue that I explain later in the paragraph.


Ah, my apologies! I am also learning Ember, so I am not entirely sure. Hopefully someone else can come along and enlighten both of us.


I have a large, complex Ember.js + Ember-Data + Flask API app, and I don't think that I use `needs` anywhere in the codebase, so I don't think it's absolutely necessary. IIRC, the documentation says that `needs` only applies to singleton controllers. If you have a controller associated with a model (vs. a view + controller + route combo), I would assume that it's not a singleton (i.e. controllers associated with a view are singletons in that Controller.init() is only called once, even when switching away and back to a view).


To use my example of an app with URLs like "/tv_shows/555/seasons/3/episodes/2", if you're in the episodes/index controller (or template or view), how do you reference the particular season and tv_show that episode is part of?


If your API supports it you can use the EmbeddedRecordsMixin[0]. In general though, what would be a sane default for handling multiple REST API calls to single-model endpoints?

[0]: http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin....


As a new comer to Ember, I am quite confused as to the necessity of the Rails portion of the tutorial. I read through the intro and the first chapter and all I could gather was that it was for the "back end". I really wish there was a great, up-to-date tutorial that was back-end agnostic so that newbies don't have to possibly learn two new frameworks at once.


Totally agreed. I went glassy eyed at "First create new rvm gemset to sandbox our gems:"

If you are a RoR person, I'm sure this tutorial is just great. But for the rest of us...


That's a valid complaint. I wrote it with a Rails backend because that's a pretty popular backend choice for Ember, and they work together well.

Perhaps I can just extract out the Ember portion -- It should still be applicable regardless of the backend.


Do you even /need/ a backend though?

It would be much simpler if you keep it entirely client side, using something like Pretender (https://github.com/trek/pretender).


To persist data you'll want a backend. That's a fairly important piece of any application which is why I included it in the tutorial. You could ignore the Rails parts of the tutorial and create your own backend.


Persist across clients, maybe, but it's also possible to use localStorage to persist data on a single client.


You could also use something like Firebase as a datastore that could persist across multiple clients but also be implemented in javascript.


Maybe stub out the backend with an Express app? Express seems simple enough that even if you're not familiar with it, you can still understand what it's trying to do.


And you'd keep most of the code in javascript so new comers wouldn't get "culture shock" by having to learn completely new syntax. +1


Would you consider Parse/Firebase?


What's funny about that is that step is completely unnecessary. At least, I have never had to create a gemset and I have had up to 20 different Ruby applications on my machine at a time. Bundler solved this problem.


It's pretty tough to make a tutorial for literally everyone. Heck, some people use CoffeeScript and some use JavaScript, and any of them might be upset if your tutorial uses the other.

If someone wrote a plain Ember tutorial that just assumed the backend API already existed, just as many people might make the complaint "if you already have a backend, I'm sure this tutorial is just great, but for the rest of us..."


My favourite Ember (Data) quirk:

  this.get('store').createRecord('ModelName', {
    .. attrs ..
  }).save().then(function (model) {
    // Ember.isNone(model) === true
  }, function(error) {
    console.error(error);
  });
The fix: s/ModelName/modelName/

This works sometimes, and not others. The only indication of failure is the fact that the model is not loaded from the JSON response to the POST/PUT request. It's a subtle bug. I wasted a bunch of time tracking this down through the Ember Data internals (for a co-worker, but the original bug/typo could easily have been mine).

The other quirk is converting snake case to camel case and back:

  address_1 =(to camelcase)=> address1 =(to snakecase)=> address1
Oops! We only use capital letters to determine where underscores go! ;-)


This should be quite easy to fix, one would think. If the name can't resolve to a model class then it should be very vocal about it (i.e. throw error). I think you should create an issue for it on Github if there's not one already.


The funny thing was that the only thing preventing it from working (even though it shouldn't) was a one or two line change in the internal code that loads payloads into models. I actually thought that it was a bug until I realized what was happening (though it was still difficult because one of the methods that was being used is/was monkey-patched on at runtime, and therefore don't exist on any of the classes in the code).


You shouldn't use Monaco as your only font for your pre and code elements. It comes up as sans-serif on windows and linux...


Hmm.. what's a good alternative font?


font-family: "Monaco", "Consolas", "Ubuntu Mono", "whatever", "other", "fonts", monospace;


Thx. Fixed.


Just finishing my first app using Ember and don't think that i'll go that route again. Speaking of which - this controller-route separation feels a bit unnatural. At least the way it's implemented.

There are lots of good parts to it - that same convention of configuration saves lots of boilerplate, data binding is usually really nice, overal separation of concerns when doing it ember-way is rather good (far from great though), but lots of small nuisances here and there make up for a dubious overall experience.


Good work, bro! Main criticism is that the 1st chapter took forever though.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: