Skip to content

Instantly share code, notes, and snippets.

@lancejpollard
Created May 22, 2012 06:32
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save lancejpollard/2767098 to your computer and use it in GitHub Desktop.
Save lancejpollard/2767098 to your computer and use it in GitHub Desktop.
Notes on Ember.js overcomplicating...

Some random notes on the pangs of ember. Will be expanding as they are uncovered.

Building a form

Say you have a form that maps to a model, something like:

<form>
  <fieldset>
    <legend>Who are you?</legend>
    <ol>
      <li class='field string required validate'>
        <label>First Name</label>
        <input name='user[first_name]'/>
      </li>
      <li class='field string required validate'>
        <label>Last Name</label>
        <input name='user[last_name]'/>
      </li>
      <li class='field string required validate'>
        <label>Email</label>
        <input name='user[email]'/>
      </li>
    </ol>
  </fieldset>
</form>

To handle this in jQuery all you have to do is this:

$(function() {
  $('#user-form').submit(function() {
    var form  = $(this);
    var data  = {};
    var id    = form.attr('id');
    var user  = App.User.find(id);
    
    $('.field', this).each(function() {
      // really, some `serialize()` function...
      var name    = $(this).attr('name');
      var value   = $(this).val();
      data[name]  = value;
    });
    
    user.updateAttributes(data);
    
    if (!user.valid()) {
      for (var key in user.errors) {
        // firstName-input (pretend at least)
        $('#' + key + '-input', form).addClass('invalid')
          .append("<output class='error'>" + user.errors[key].join("\n") + "</output>");
      }
      return false;
    }
  });
});

... it took me like 5 minutes to write that, and I'm sure I can refactor that to something reusable in any app in an hour or two. Maybe even make it a plugin on Github, another 1 or 2 hours...

Now to make that same thing in Ember...

  1. Build the HTML form using Handlebars markup.
  2. Build the views
  3. Build the controllers

Not only that, think about orders of magnitude more variables.

First, build the HTML, simple enough (and getting excited because of how simple it seems):

{{#with App.newUser}}
  <form>
    <fieldset>
      <legend>Who are you?</legend>
      <ol>
        <li class='field string required validate'>
          <label>First Name</label>
          {{view Ember.TextField valueBinding="first_name"}}
        </li>
        <li class='field string required validate'>
          <label>Last Name</label>
          {{view Ember.TextField valueBinding="last_name"}}
        </li>
        <li class='field string required validate'>
          <label>Email</label>
          {{view Ember.TextField valueBinding="email"}}
        </li>
      </ol>
    </fieldset>
  </form>
{{/with}}

Now, I don't want the model to update every time I enter something in the keyboard. After reading through docs and source code for a few minutes, I realize I can just change

{{view Ember.TextField valueBinding="email"}}

to

<input type='text' {{bindAttr value="email"}} />

But now in the first case, I'm using an Ember.View object, and in the second case, am I using an Ember.View instance? Not clear, so look it up in the docs. Oh, and if I remove that <input...> element, what's going to happen do the bindings? Now all of a sudden I can't do the quick-and-easy $('input').remove(). Instead, I have to do Ember.View.VIEWS[$('input[name="email"]').attr('id')]].destroy(). That's pretty ugly. There's a way around that though, no worries! All you have to do is make the <form> an Ember.View, or wrap it all in an Ember.View as a template.

But now, I want to start adding something like an autocomplete box, or the stackoverflow-like tag box. In jQuery I can just listen to the keyup event, run some ajax, and position some divs over the text field:

$('input').keyup(function() {
  var input     = $(this);
  var position  = input.offset();
  $.ajax({
    url: '/autocomplete',
    data: {query: $(this).val()},
    success: function(words) {
      var divs = [];
      for (var i = 0; i < words.length; i++) {
        divs.push('<div>' + words[i] + '</div>');
      };
      $('#popup').empty().append(divs.join('\n'));
    }
  });
});

Again, took 2-3 minutes to whip together that function.

For Ember what do I have to do? I have to create an App.AutocompleteView, and should that be an Ember.ContainerView or Ember.CollectionView? Then I have to think about how and where the event handlers go (for clicking on the tag in the autocomplete box, for example).

Anyway, all of a sudden, with Ember, I have to have a full-on set of UIComponents, with very rich event handling systems, like flamejs:

  • alert_panel.js
  • button_view.js
  • checkbox_view.js
  • collection_view.js
  • disclosure_view.js
  • form_view.js
  • horizontal_split_view.js
  • image_view.js
  • label_view.js
  • list_item_view.js
  • list_view.js
  • list_view_drag_helper.js
  • loading_indicator_view.js
  • menu_view.js
  • panel.js
  • popover.js
  • progress_view.js
  • radio_button_view.js
  • root_view.js
  • scroll_view.js
  • search_text_field_view.js
  • select_button_view.js
  • stack_item_view.js
  • stack_view.js
  • tab_view.js
  • table_data_view.js
  • table_view.js
  • text_area_view.js
  • text_field_view.js
  • tree_item_view.js
  • tree_view.js
  • vertical_split_view.js

And those aren't easy classes to make, they're pretty involved. And now I'm up to 300KB of JavaScript just to get back to the ability to do basic things I could do in a few lines of jQuery.

Anyway, I'm not going to spend the time describing the details of all this, it would take hours/days. All I'm going to say now is I feel like I have to build basically sproutcore and then some on top of Ember.js in order to get back to what I could do with jQuery 5 minutes with only a few lines of code.

@gobengo
Copy link

gobengo commented May 22, 2012

So what's your opinion on it's practical use?

Presumably the counterarguments goes: "For complex apps, larger teams, and when robustness is key, the extra overhead of development has a lower cost to the organization as a whole". Now, I haven't used Ember, but I'm curious how for any project you might decide to use jQuery vs Ember. Would you always go jQuery?

@lancejpollard
Copy link
Author

Yeah that seems about right:

For complex apps, larger teams, and when robustness is key, the extra overhead of development has a lower cost to the organization as a whole.

But then the project's starting to sound enterprise.

Personally, I won't use Ember.js for any personal projects. I disagree with the slogan on emberjs.com:

Ember.js is built for productivity. Designed with developer ergonomics in mind, its friendly APIs help you get your job done—fast.

The type of productivity they're targeting is, I feel, the same market as the DreamWeaver users, the ones who don't really know how to code but who want to make things quickly without much effort. The thing is, no [good] developers use DreamWeaver, because despite its high intentions, it got in the way more than it helped. It turns out that TextMate and Vim increase productivity - contrary to what you'd expect on the surface.

However, if any of my apps need 100% data-binding, meaning I want every input and list item to reflect a change in a model field, and the model field to reflect the changes in the input or list item, then I will use Ember.js. But the reality is, that's only good for demos. Unless your app is a chat application demoing data-binding, you don't need a built-in data-binding solution. It will just get in your way. Instead of writing new code to build out more of your app (or to have the time to learn something new like Hadoop), you'll be figuring out how to carefully construct your code so it works with data-binding. Hours and days go by like this. And it feels productive. It's insanely seductive. But really, you're just spending your life's energy trying to understand all of the implications of your data-binding code. I will use my energy instead to either learn or create things.

Ember.js seems perfect in principle. I can't argue with that. @d4tocchini and I have talked for years about this (especially about MVC in general). But in practice, at least for me, it only wastes precious time. Sure you can get a demo going in 5 minutes that gets you all excited, where it might take you 2 or 3 days in plain jQuery. But spend a couple weeks on your project and look back: if you went the Ember.js route, most likely you spent the majority of your time debugging and trying to figure out how to fit your code into your app. If you went the plain jQuery route (or Backbone, or Spine, anything without complex Views/Components), you will be done with your app mastering the next thing in your field.

The tendency, or seductive tendency, is to keep digging further and further into data-binding, because it feels right. It is right, in principle. But if you track your results like I do, you'll quickly see you've wasted a lot of valuable time.

I hope this is because I have just not mastered this whole stateful UI thing (like the iPhone Cocoa MVC framework), and in a few months I'll be raving about it's benefits. But, I spent years in Flex, and I always thought what we were doing was amazing, but really, I was spending more and more and more of my time trying to figure out how to build Views/Components and debug performance problems, which is the main reason I stopped using Flex and started using jQuery and plain HTML5.

@dgeb
Copy link

dgeb commented May 22, 2012

I should start by saying that it's still too difficult to determine best practices when using Ember, and that this will hopefully be resolved with more blog posts and Ember guides. I've been working with Ember for a few months and have some recommendations.

I would start by examining the reason you'd like to avoid bindings in your form. Perhaps you want to only apply changes after a user clicks an Update button? One approach is to create a copy of your model which is then bound to your form, and the attributes of that model are then applied to the original model upon success. Here's a simple example using ember-rest. Perhaps better yet, using ember-data, you could wrap changes in a transaction which is only committed when your form is submitted, like in this ember-data example. (Note: both of these examples will be cleaned up with the new conventions being introduced in Ember 1.0 - see below).

However, if you really don't want any bindings (and you want to pull values from the form on submit), you could just use an unbound value:

<input type='text' name='user[email]' value="{{unbound email}}" />

Unbound values won't configure any binding logic and therefore don't require any teardown code. Even if you were to take a bound approach, you still shouldn't need to manually destroy the input. It would be better to wrap the logic which determines its presence in your template. For example:

{{#if canHazEmail}}
  {{view Ember.TextField valueBinding="email"}}
{{/if}}

I don't think that Ember requires a "full-on set of UIComponents". The Ember core team is specifically trying to avoid rebuilding Sproutcore. UI libs like Flame.js have their place, just as libs like jQuery UI do, but such a lib is in no way required for using Ember. It is one of the specific goals of Ember core to only include HTML4 native controls where bindings or custom event handling are common. The general preference is to defer to standard HTML elements and use helpers such as {{bindAttr}} and {{action}} where appropriate. This is the reason that Ember.Button was recently removed from core, now that <button {{action}} /> is effectively the same.

For your autocomplete box, don't forget that you could still configure it with jQuery from within Ember. You would need to do so from within your view's didInsertElement() event, which indicates that the view has been inserted into the DOM. However, if you want to reuse your logic and generalize your data bindings, creating an AutocompleteView would be an elegant way to go.

Last but not least, I think that the work being done now in Ember regarding view / controller relationships, state managers, and routing is pretty exciting. These are problems that all complex JS MVC apps face, and Ember will try to tackle them together in a cohesive way that provides clear conventions for developers. Because they won't be handled by different libs, I think their inclusion in Ember will actually decrease total js size for most apps. I'm hoping the advantages of Ember will become really clear as 1.0 takes shape and more end-to-end, complex examples and guides are written.

@wycats
Copy link

wycats commented May 22, 2012

@viatropos without going into detail this second (more later, when I get a second), I'll say that your critique is valid. While Ember's primitives are very solid, there are still common cases where figuring out how to wire them up is non-trivial. Your case is a combination of missing features and documentation, and it reflects a pretty common pattern that should be easier to figure out in Ember.

The benefit that you get with Ember (when things go right) is similar to the benefit you get with unit testing. It's a bit more effort in the beginning, but it pays for itself quickly when you add new features. Your jQuery example would work (and I've written plenty of examples like it), but it composes extremely poorly with future features that work with the same data.

Ember shines when working with multiple areas of the screen that reflect different views of the same data (e.g. a list of things and a separate count of those things). That type of scenario shows up repeatedly in every app I've ever worked on, and is the source of unending, irritating bugs.

@trek
Copy link

trek commented May 22, 2012

@viatropos It seems like your main complaint, rightly, is that it's still very hard to figure out how to best use Ember. I know people are working hard to remedy that and better expose good Ember patterns.

If you went the plain jQuery route ... you will be done with your app mastering the next thing in your field.

Ignores the implicit fact that you already know jQuery and have spent time learning how to best put together applications using it. There are many, many people who haven't walked that road yet and will spend the next few years stumbling through worst practices and anti-patterns as they begin writing stateful, in-browser applications for their job, clients, or fun.

Your frustration is identical to what was voiced for Rails in 2005: "Why bother, I can do this better/faster/easier in PHP/Java/C#." It was equally valid for Rails in 2005 as it is for Ember now. Yes, a person can use their existing skills and tools to put together something better and faster then learning a new set of tools and skills and then building – especially when the resources for learning are as scare as Ember's.

But the counter point is the same for both frameworks: Ember is an attempt to gather already discovered good ways of accomplishing a goal so that everyone doesn't have to go through the same rough experimentation.

You simply cannot judge the efficacy or usefulness of a framework by your first few uses: it will always compare poorly. Being an early adopter is rough because there's a total lack external validation that spending the time to become skilled will have enough payoff. You kind of have to go with your gut.

My gut tells me that Ember is the winner in its category. Its patterns have a distinct correct solidness to them that other frameworks lack, which is why I started committing to it. If you don't agree or aren't sure, give the project sometime to build up a reputation for success before returning and investing the time.

Best of all: the longer you wait the better the framework and available resources for learning will become.

@wagenet
Copy link

wagenet commented May 25, 2012

I think you're also running into the problem that good app development is hard, period. Ember can make it better, but you'll never be able to develop a good app in your sleep. There's always going to be lots of work to plan your app and architect it properly. Of course, if you don't care about making a really solid app and just want to hack something together, Ember is certainly not the easiest way. However, I imagine that, like the rest of us, you actually want to make apps that are good. Be careful that you don't blame Ember for something inherent in the process. That said, I do think there's more we can do to make the process easier, but I think it's naive to expect that building apps will ever truly be easy.

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