Ember's official documentation describes a number of low-level APIs, but doesn't offer advice on how to put them together. As a result, a simple task such as creating a simple CRUD application is not obvious to a newcomer.
To help solving this problem, I decided to figure out and document a clear convention for simple CRUD apps, using Ember and Ember Data with no third-party add-ons.
I hope this will serve as a starting point for beginners, showing common conventions, idioms and patterns in use by the Ember community. I also hope to benefit myself personally, when readers point out mistakes (which will be corrected) and bring up differing opinions (which will be considered).
This implementation is heavily influenced by the style of Ruby on Rails CRUDs, with which I am most familiar.
(Incidentally, Ruby on Rails does a great job of communicating its basic CRUD convention. Not only through its documentation, but also with tools such as the scaffolding generator.)
This is what I expect from this convention:
- This CRUD assumes that each action will take place in a different page/view/route
- Records will be persisted to the server using Ember Data. The adapter/serializer parts are supposed to be working and are not relevant
- Validation will happen server-side
- The interface must be accomodate for the possibility of validation errors
This code could use mixins and components to avoid repetition. However I am avoiding this because:
- I want this as a simple, readable example with minimum complexity
- This example can serve as a starting point for more complex applications where there's no such duplication
If you use this code, you may want to DRY it up as suggested.
This example assumes the model is called line
. It's defined
as an Ember Data model and it only has one attribute: name
,
which is a string.
Following Ruby on Rails's lead, the paths/routes for each CRUD acion are the following:
/lines
- list existing records/lines/new
- create a new record/lines/:id
- show a single record/lines/:id/edit
- edit and update a single record/lines/:id/destroy
- delete a record, but confirm first
When detecting a move away from the route, prefer willTransition
over deactivate
. This is because the latter doesn't fire
when only the model changes.
This may not sound relevant, but consider the following example.
Say you extend the edit
route to show a list of existing records
(like the index
route). As you edit the record, you'll see
it updating on the list, which is pretty cool. However, if you:
- Edit a record
- Use the list to navigate away to another record
- Click cancel to return to
index
The original record will remain edited (not rolled back), but won't have been persisted. After reloading the page, the change will disappear.
To discard an newly created, unsaved record, use Store#unloadRecord
.
From the guides, it would appear that Model#deleteRecord
and
Model#destroyRecord
might be a better bet. However, they
have these problems:
Model#deleteRecord
: it doesn't work when the record is unsaved but has errors. Ie: the user filled out the form, the app tried to save, server-side validation returned errors, the model had itsModel#errors
populated.Model#destroyRecord
: same asdeleteRecord
, but also tries to persist the deletion of this actually-not-persisted record. For this, it makes a request toDELETE /{model-name}/{id}
but, since there's no id yet, it ends up beingDELETE /{model-name}
.
This convention may change in the future, as the strange
behaviour of deleteRecord
is a bug, acknowledged at emberjs/data#4289
Still, I have a preference for Store#unloadRecord
as it mirrors
the previous Store#createRecord
.
It's possible to achieve the same effect using Model#rollbackAttributes
:
- Advantages: it's the same API used in the
edit
route, making it easier to refactor both into a single mixin - Drawbacks: it may not communicate its intent as well as other options,
because is sounds like the attributes are restored, but the record is
not deleted (when it actually is). Also, it doesn't mirror
Store#createRecord
either.
Resources I have found or been pointed to. I'm currently going through them:
- A List of Open Source EmberJS Applications by Iheanyi Ekechukwu
- Ember.js 2 Tutorial by Zoltan Debre
- ember-cli-scaffold by Marcio Junior
This is a work in progress.