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!
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!
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'),2. The typical pattern is lots of computed properties. They're lazily calculated, so that makes them pretty cheap.
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.
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') });
});[0]: http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin....
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.
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).
† Plus the enforced snakeCase/CamelCase everywhere, but that's more to do with me far preferring names_with_underscores in general.
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...
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!