Skip to content

Instantly share code, notes, and snippets.

@pablobm
Last active June 3, 2022 10:07
Show Gist options
  • Select an option

  • Save pablobm/e77a98e5f3c610953a82 to your computer and use it in GitHub Desktop.

Select an option

Save pablobm/e77a98e5f3c610953a82 to your computer and use it in GitHub Desktop.

Revisions

  1. pablobm revised this gist Oct 23, 2016. 1 changed file with 19 additions and 6 deletions.
    25 changes: 19 additions & 6 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -319,12 +319,6 @@ Still, `Model#rollbackAttributes` is the best way to do it. It is
    usable in two different situations, and it saves you checking
    for `record.get('isNew')`.

    ## Validation errors on `base`

    Remember that validation errors can be associated to a record's `base`,
    not only to its properties. You may want to check if there's anything
    to display on `model.errors.base`.

    ## Alternative: use buffer object as models

    Using `Store#createRecord` on the `new` route has two gotchas:
    @@ -371,6 +365,25 @@ However, this doesn't entirely work:
    anyway, and it will appear on the list after transitioning
    away.

    ## Validation errors on the object as a whole

    Remember that validation errors can be associated to a record as a whole,
    as opposed to individual fields. Maybe you were trying to buy an item
    that run out, or there's an invalid interaction between several fields
    that cannot be blamed on a specific one. This would be communicated to the
    user with a "general" error message instead of flagging a section of a form.

    As far as I know, there's no common convention for addressing this, and
    different backend frameworks may (or may not) offer support for it in
    different ways. For example, Ruby on Rails used to address this case with
    an explicit API, but this has since been dropped. Nowadays, you would be
    expected to list these errors as if they were part of a field that is not
    really on the form.

    Your code may want to check on this pseudo-attribute too. In Ruby on Rails,
    this is normally called `base`, so in this case you'd want to make
    sure to display any errors on `model.errors.base`.

    ## See also...

    Resources I have found or been pointed to. I'm currently going through them:
  2. Pablo Brasero revised this gist Oct 21, 2016. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -241,12 +241,15 @@ losing the state of the app. Avoid.

    Do use closure actions with Ember components though. They are great.

    ## `willTransition`
    ## `willTransition` and `didTransition`

    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.

    For the same reason, to detect a successful landing on a route, prefer
    `didTransition` over `activate`.

    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
  3. Pablo Brasero revised this gist Jul 1, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -79,7 +79,7 @@ name of the model. This is, they read `:line_id` instead of just `:id`.
    This is because:

    * The default implementation of `Route#model` (the `model()` hook in routes)
    expects this name (see "The model() hook" below).
    expects this name (see "The `model()` hook" below).
    * It is necessary anyway when working with nested models. Eg:
    `/receipt/:receipt_id/lines/:line_id`

  4. Pablo Brasero revised this gist Jul 1, 2016. 5 changed files with 45 additions and 25 deletions.
    45 changes: 40 additions & 5 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -70,9 +70,18 @@ action 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
    * `/lines/:line_id` - show a single record
    * `/lines/:line_id/edit` - edit and update a single record
    * `/lines/:line_id/destroy` - delete a record, but confirm first

    Unlike the usual basic Rails paths though, the `id` segments include the
    name of the model. This is, they read `:line_id` instead of just `:id`.
    This is because:

    * The default implementation of `Route#model` (the `model()` hook in routes)
    expects this name (see "The model() hook" below).
    * It is necessary anyway when working with nested models. Eg:
    `/receipt/:receipt_id/lines/:line_id`

    ## The `index` route

    @@ -97,9 +106,35 @@ this.transitionTo('lines');
    this.transitionTo('lines.index');
    ```

    ## The `model()` hook (or its absence)

    `Route#model()` has a default implementation that may work for you already.
    If the last segment parameter of your path (eg: `:line_id` in
    `/receipts/:receipt_id/lines/:line_id`) matches the format `:{NAME}_id`,
    Ember will assume that `{NAME}` is the name of your model, and will fetch it
    from the store with `this.store.find('{NAME}', params.{NAME}_id)`.

    Therefore, if you follow the convention of having a segment `:line_id`, you
    may not need to implement the `model()` hook at all.

    ## The `show` route can be missing too

    Following on the previous point, you may not need to provide an explicit
    `show` route:

    1. Ember will provide implicit routes for any routes mentioned in
    `app/router.js`
    2. An implicit route is equivalent to an empty route. In other words: a
    route that extends `Ember.Route` but doesn't add anything to it
    3. `Ember.Route` has an implementation of the `model()` hook, which will
    be used in an empty route

    Remember that you will still need to provide a template.

    ## `find`-er methods

    Use `Store#findRecord`, not `Store#find`.
    If you still need to grab a record from the store, use `Store#findRecord`,
    not `Store#find`.

    Before Ember Data 1.13, you would retrieve records with the methods
    `Store#find`, `Store#all`, `Store#getById` and others. These have been
    @@ -119,7 +154,7 @@ not notice. However, it's marked private on Ember Data's source
    code, and usage by your code is discouraged. It has been kept
    around for compatibility reasons, because Ember (which is separate
    from Ember Data) uses it for the default implementation of the
    `Route#model` hook. Check out the source code for [more information](https://github.com/emberjs/data/blob/master/addon/-private/system/store.js#L385).
    `Route#model()` hook. Check out the source code for [more information](https://github.com/emberjs/data/blob/master/addon/-private/system/store.js#L385).

    ## Accessing the current model

    8 changes: 4 additions & 4 deletions app..router.js
    Original file line number Diff line number Diff line change
    @@ -8,10 +8,10 @@ const Router = Ember.Router.extend({
    Router.map(function() {
    this.route('lines', function() {
    this.route('new');
    this.route('show', {path: ':id'});
    this.route('edit', {path: ':id/edit'});
    this.route('destroy', {path: ':id/destroy'});
    this.route('show', {path: ':line_id'});
    this.route('edit', {path: ':line_id/edit'});
    this.route('destroy', {path: ':line_id/destroy'});
    });
    });

    export default Router;
    export default Router;
    6 changes: 1 addition & 5 deletions app..routes..lines..destroy.js
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,10 @@
    import Ember from 'ember';

    export default Ember.Route.extend({
    model(params) {
    return this.store.findRecord('line', params.id);
    },

    actions: {
    confirm(record) {
    record.destroyRecord()
    .then(() => this.transitionTo('lines'));
    },
    }
    });
    });
    4 changes: 0 additions & 4 deletions app..routes..lines..edit.js
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,6 @@
    import Ember from 'ember';

    export default Ember.Route.extend({
    model(params) {
    return this.store.findRecord('line', params.id);
    },

    actions: {
    save(record) {
    record.save()
    7 changes: 0 additions & 7 deletions app..routes..lines..show.js
    Original file line number Diff line number Diff line change
    @@ -1,7 +0,0 @@
    import Ember from 'ember';

    export default Ember.Route.extend({
    model(params) {
    return this.store.findRecord('line', params.id);
    },
    }
  5. pablobm revised this gist May 29, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -233,7 +233,7 @@ Quoth @rwjblue (https://youtu.be/7kU5lLnEtJs?t=4m24s):
    > framework hook. Period. End of story. Even if you know it
    > doesn’t need to be done, you should do it.
    Therefore, always `this._super(...arguments)` on Ember hooks.
    Therefore, always call `this._super(...arguments)` on Ember hooks.
    This includes `willTransition`.

    ## Discarding changes to a record
  6. pablobm revised this gist May 29, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -200,7 +200,7 @@ action as follows:
    <form onsubmit={{action 'confirm' model}}>
    ```

    This will not stop the `submit` event in anyway. As a result, the
    This will not stop the `submit` event in any way. As a result, the
    browser will attempt to submit the form, causing a page refresh and
    losing the state of the app. Avoid.

  7. pablobm revised this gist May 29, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -66,7 +66,7 @@ which is a string.
    ## Routes

    Following Ruby on Rails's lead, the paths/routes for each CRUD
    acion are the following:
    action are the following:

    * `/lines` - list existing records
    * `/lines/new` - create a new record
  8. Pablo Brasero revised this gist May 12, 2016. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,8 @@
    # CRUD with Ember (+ Data)

    ![Compatible with Ember 1.13.0+](https://embadge.io/v1/badge.svg?start=1.13.0)
    ![Compatible with Ember Data 1.13.0+](https://embadge.io/v1/badge.svg?start=1.13.0&label=ember-data)

    Ember's official documentation describes a number of low-level
    APIs, but doesn't talk much about how to put them together.
    As a result, a simple task such as creating a simple CRUD
  9. Pablo Brasero revised this gist May 12, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@ and document a clear convention for simple CRUD apps,
    using Ember and Ember Data with no third-party add-ons.

    I hope this will be a useful resource for beginners who,
    fresh out of reading the [Guides](https://guides.emberjs.com/v2.4.0/),
    fresh out of reading the [Guides](https://guides.emberjs.com),
    could use more directions on basic conventions, idioms and
    patterns in use by the Ember community. I also hope to benefit
    myself personally, when readers point out mistakes (which will
  10. Pablo Brasero revised this gist May 11, 2016. 1 changed file with 25 additions and 0 deletions.
    25 changes: 25 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -178,6 +178,31 @@ to get forms to work as they should:
    This should get you some extra usability and accessibility brownie
    points, if only because that's the way forms are intended to work.

    ## Capture DOM events with "classic" actions

    DOM events, such as `click` or `submit` (among others) should be
    captured using "classic" actions, as opposed to "closure" actions.
    Example follows:

    ```handlebars
    <form {{action 'confirm' model on='submit'}}>
    ```

    This is because classic actions stop the propagation and the default
    behaviour of these events, whereas closure actions don't. For
    example, say that we capture the `submit` event with a closure
    action as follows:

    ```handlebars
    <form onsubmit={{action 'confirm' model}}>
    ```

    This will not stop the `submit` event in anyway. As a result, the
    browser will attempt to submit the form, causing a page refresh and
    losing the state of the app. Avoid.

    Do use closure actions with Ember components though. They are great.

    ## `willTransition`

    When detecting a move away from the route, prefer `willTransition`
  11. Pablo Brasero revised this gist May 11, 2016. 2 changed files with 8 additions and 5 deletions.
    9 changes: 5 additions & 4 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -154,16 +154,17 @@ purpose:
    initially it will work but, since it's a private API, who knows
    how it could break in the future.

    ## Trigger `save` on form submit
    ## Trigger actions on form submission

    It's tempting to associate the `save` action with clicking a
    button:
    On forms, it's tempting to associate the `save` action (or similar)
    with clicking a button. In fact, that's what the guides teach you
    to do:

    ```html
    <button {{action 'save' model}}>Save</button>
    ```

    But then we cannot press *Enter* on the text field to save. Remember
    But then we cannot press *Enter* on text fields to save. Remember
    that, on the web, data is submitted on a `submit` event. Use this
    to get forms to work as they should:

    4 changes: 3 additions & 1 deletion app..templates..lines..destroy.hbs
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    <p>Are you sure?</p>

    <p><button {{action 'confirm' model}}>Delete <strong>{{model.name}}</strong></button> or {{#link-to 'lines'}}cancel{{/link-to}}</p>
    <form {{action 'confirm' model on='submit'}}>
    <p><button>Delete <strong>{{model.name}}</strong></button> or {{#link-to 'lines'}}cancel{{/link-to}}</p>
    </form>
  12. Pablo Brasero revised this gist May 11, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -236,7 +236,7 @@ if (record.get('isNew')) {
    Now, to actually delete the record, we have these options:

    * `Model#deleteRecord`: between Ember Data v2.0.0.beta1 and v2.5.0,
    and bug made this not work correctly when the record is unsaved
    a bug made this not work correctly when the record is unsaved
    but has errors. Ie: the user fills out the form, the app tries
    to save, server-side validation returns errors, the model
    has its `Model#errors` populated.
  13. Pablo Brasero revised this gist May 11, 2016. 2 changed files with 38 additions and 31 deletions.
    65 changes: 37 additions & 28 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -207,41 +207,50 @@ Quoth @rwjblue (https://youtu.be/7kU5lLnEtJs?t=4m24s):
    Therefore, always `this._super(...arguments)` on Ember hooks.
    This includes `willTransition`.

    ## `unloadRecord`
    ## Discarding changes to a record

    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:
    To discard changes to a record, use `Model#rollbackAttributes`:

    * When updating an existing record, it will discard any unsaved
    changes.
    * When creating a new record, it will drop it from the store.

    Since it works in both cases, it opens up new opportunities for
    reusing code. For example in the form of mixins.

    There's something slightly annoying with the name `rollbackAttributes`,
    and it is that it conveys the meaning "discard unsaved changes", but
    not so much "drop unsaved records". Still, this is the accepted way
    of doing things, and is documented on the guides and the API docs.

    For the specific case of dropping unsaved records, there are other
    alternatives. You can check if the record has been saved, and delete
    it if not. It would be something like this:

    ```js
    if (record.get('isNew')) {
    // Delete the record here
    }
    ```

    Now, to actually delete the record, we have these options:

    * `Model#deleteRecord`: between Ember Data v2.0.0.beta1 and v2.5.0,
    and bug made this not work correctly when the record is unsaved
    but has errors. Ie: the user fills out the form, the app tries
    to save, server-side validation returns errors, the model
    has its `Model#errors` populated.

    * `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 its `Model#errors` populated.
    * `Model#destroyRecord`: same as `deleteRecord`, but also tries to
    persist the deletion of this actually-not-persisted record.
    For this, it makes a request to `DELETE /{model-name}/{id}` but,
    since there's no id yet, it ends up being `DELETE /{model-name}`.

    This convention may change in the future, as the strange
    behaviour of `deleteRecord` is a bug, acknowledged at https://github.com/emberjs/data/issues/4289
    Still, I have a preference for `Store#unloadRecord` as it mirrors
    the previous `Store#createRecord`.

    ### Other alternatives

    It's possible to achieve the same effect using `Model#rollbackAttributes`:

    * Advantages:
    1. It's the same API used in the `edit` route, making it
    easier to refactor both into a single mixin
    2. It allows us to remove the conditional that checks for `record.get('isNew')`.
    In fact we can reduce that function to a one-liner: `this.controller.get('model').rollbackAttributes();`
    * Drawbacks:
    1. I feel it doesn't communicate its intent as well as other options,
    because is sounds like the attributes are restored (to blank), but the
    record is not deleted (when it actually is)
    2. It doesn't mirror `Store#createRecord` like `Store#unloadRecord` does
    * `Store#unloadRecord`: this actually works well

    Still, `Model#rollbackAttributes` is the best way to do it. It is
    usable in two different situations, and it saves you checking
    for `record.get('isNew')`.

    ## Validation errors on `base`

    4 changes: 1 addition & 3 deletions app..routes..lines..new.js
    Original file line number Diff line number Diff line change
    @@ -14,9 +14,7 @@ export default Ember.Route.extend({
    willTransition() {
    this._super(...arguments);
    const record = this.controller.get('model');
    if (record.get('isNew')) {
    this.store.unloadRecord(record);
    }
    record.rollbackAttributes();
    },
    },
    });
  14. Pablo Brasero revised this gist May 11, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -40,7 +40,7 @@ This is what I expect from this convention:
    The adapter/serializer parts are supposed to be working
    and are not relevant
    3. Validation will happen server-side
    4. The interface must be accomodate for the possibility
    4. The interface will accomodate for the possibility
    of validation errors

    ## This is not very DRY
  15. Pablo Brasero revised this gist Apr 25, 2016. 1 changed file with 24 additions and 24 deletions.
    48 changes: 24 additions & 24 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -60,30 +60,6 @@ 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.

    ## `find`-er methods

    Use `Store#findRecord`, not `Store#find`.

    Before Ember Data 1.13, you would retrieve records with the methods
    `Store#find`, `Store#all`, `Store#getById` and others. These have been
    deprecated and removed.

    These are the methods you should be using now, as seen on the
    [Ember Data 1.13 release announcement](http://emberjs.com/blog/2015/06/18/ember-data-1-13-released.html):

    | | Async from server/store | Sync from store | Query server |
    |---------------|-------------------------|----------------------|----------------------------|
    | Single Record | findRecord(type,id) | peekRecord(type, id) | queryRecord(type, {query}) |
    | All Records | findAll(type) | peekAll(type) | query(type, {query}) |

    I told a little lie though: `Store#find` hasn't been removed, and it
    won't even trigger a deprecation alert. You could use it and would
    not notice. However, it's marked private on Ember Data's source
    code, and usage by your code is discouraged. It has been kept
    around for compatibility reasons, because Ember (which is separate
    from Ember Data) uses it for the default implementation of the
    `Route#model` hook. Check out the source code for [more information](https://github.com/emberjs/data/blob/master/addon/-private/system/store.js#L385).

    ## Routes

    Following Ruby on Rails's lead, the paths/routes for each CRUD
    @@ -118,6 +94,30 @@ this.transitionTo('lines');
    this.transitionTo('lines.index');
    ```

    ## `find`-er methods

    Use `Store#findRecord`, not `Store#find`.

    Before Ember Data 1.13, you would retrieve records with the methods
    `Store#find`, `Store#all`, `Store#getById` and others. These have been
    deprecated and removed.

    These are the methods you should be using now, as seen on the
    [Ember Data 1.13 release announcement](http://emberjs.com/blog/2015/06/18/ember-data-1-13-released.html):

    | | Async from server/store | Sync from store | Query server |
    |---------------|-------------------------|----------------------|----------------------------|
    | Single Record | findRecord(type,id) | peekRecord(type, id) | queryRecord(type, {query}) |
    | All Records | findAll(type) | peekAll(type) | query(type, {query}) |

    I told a little lie though: `Store#find` hasn't been removed, and it
    won't even trigger a deprecation alert. You could use it and would
    not notice. However, it's marked private on Ember Data's source
    code, and usage by your code is discouraged. It has been kept
    around for compatibility reasons, because Ember (which is separate
    from Ember Data) uses it for the default implementation of the
    `Route#model` hook. Check out the source code for [more information](https://github.com/emberjs/data/blob/master/addon/-private/system/store.js#L385).

    ## Accessing the current model

    There seem to be two accepted ways of obtaining the current model
  16. Pablo Brasero revised this gist Apr 20, 2016. 1 changed file with 46 additions and 0 deletions.
    46 changes: 46 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -249,6 +249,52 @@ Remember that validation errors can be associated to a record's `base`,
    not only to its properties. You may want to check if there's anything
    to display on `model.errors.base`.

    ## Alternative: use buffer object as models

    Using `Store#createRecord` on the `new` route has two gotchas:

    1. As seen above, we have to remember to undo the changes if we
    cancel (ie: transition away without saving)

    2. If we are showing a list of records on the same page
    where the creation form is, we'll see the "ghost", new record
    appear unless we explicitly filter it out

    Some people then prefer using a "buffer" object that holds the values
    for the new object, without adding it to the store until the user
    decides to save. This can be done with a plain JS object:

    ```js
    import Ember from 'ember';

    export default Ember.Route.extend({
    model() {
    return {};
    },

    actions: {
    save(record) {
    const model = this.store.createRecord('line', record);
    model.save()
    .then(() => this.transitionTo('lines'));
    },
    },
    });
    ```

    Note that this tackles both issues: we don't need to worry about
    handling the `willTransition` event, and the new record won't be
    listed in the store until it's finally saved.

    However, this doesn't entirely work:

    1. Validation errors will not be added to the buffer object,
    and therefore won't be rendered on the form.

    2. On error, the record will have been added to the store
    anyway, and it will appear on the list after transitioning
    away.

    ## See also...

    Resources I have found or been pointed to. I'm currently going through them:
  17. Pablo Brasero revised this gist Apr 20, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -82,7 +82,7 @@ not notice. However, it's marked private on Ember Data's source
    code, and usage by your code is discouraged. It has been kept
    around for compatibility reasons, because Ember (which is separate
    from Ember Data) uses it for the default implementation of the
    `Route#model` hook.
    `Route#model` hook. Check out the source code for [more information](https://github.com/emberjs/data/blob/master/addon/-private/system/store.js#L385).

    ## Routes

  18. Pablo Brasero revised this gist Apr 19, 2016. 1 changed file with 24 additions and 0 deletions.
    24 changes: 24 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -60,6 +60,30 @@ 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.

    ## `find`-er methods

    Use `Store#findRecord`, not `Store#find`.

    Before Ember Data 1.13, you would retrieve records with the methods
    `Store#find`, `Store#all`, `Store#getById` and others. These have been
    deprecated and removed.

    These are the methods you should be using now, as seen on the
    [Ember Data 1.13 release announcement](http://emberjs.com/blog/2015/06/18/ember-data-1-13-released.html):

    | | Async from server/store | Sync from store | Query server |
    |---------------|-------------------------|----------------------|----------------------------|
    | Single Record | findRecord(type,id) | peekRecord(type, id) | queryRecord(type, {query}) |
    | All Records | findAll(type) | peekAll(type) | query(type, {query}) |

    I told a little lie though: `Store#find` hasn't been removed, and it
    won't even trigger a deprecation alert. You could use it and would
    not notice. However, it's marked private on Ember Data's source
    code, and usage by your code is discouraged. It has been kept
    around for compatibility reasons, because Ember (which is separate
    from Ember Data) uses it for the default implementation of the
    `Route#model` hook.

    ## Routes

    Following Ruby on Rails's lead, the paths/routes for each CRUD
  19. pablobm revised this gist Apr 19, 2016. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion app..routes..lines..destroy.js
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@ import Ember from 'ember';

    export default Ember.Route.extend({
    model(params) {
    return this.store.find('line', params.id);
    return this.store.findRecord('line', params.id);
    },

    actions: {
    2 changes: 1 addition & 1 deletion app..routes..lines..edit.js
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@ import Ember from 'ember';

    export default Ember.Route.extend({
    model(params) {
    return this.store.find('line', params.id);
    return this.store.findRecord('line', params.id);
    },

    actions: {
  20. pablobm revised this gist Apr 19, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion app..routes..lines..destroy.js
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@ import Ember from 'ember';

    export default Ember.Route.extend({
    model(params) {
    this.store.find('line', params.id);
    return this.store.find('line', params.id);
    },

    actions: {
  21. Pablo Brasero revised this gist Apr 13, 2016. 3 changed files with 15 additions and 2 deletions.
    11 changes: 11 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -172,6 +172,17 @@ The original record will remain edited (not rolled back), but
    won't have been persisted. After reloading the page, the change
    will disappear.

    ## Always call `_super` on hooks

    Quoth @rwjblue (https://youtu.be/7kU5lLnEtJs?t=4m24s):

    > You should always call `_super` whenever you implement any
    > framework hook. Period. End of story. Even if you know it
    > doesn’t need to be done, you should do it.

    Therefore, always `this._super(...arguments)` on Ember hooks.
    This includes `willTransition`.

    ## `unloadRecord`

    To discard an newly created, unsaved record, use `Store#unloadRecord`.
    3 changes: 2 additions & 1 deletion app..routes..lines..edit.js
    Original file line number Diff line number Diff line change
    @@ -12,8 +12,9 @@ export default Ember.Route.extend({
    },

    willTransition() {
    this._super(...arguments);
    const record = this.controller.get('model');
    record.rollbackAttributes();
    },
    },
    });
    });
    3 changes: 2 additions & 1 deletion app..routes..lines..new.js
    Original file line number Diff line number Diff line change
    @@ -12,10 +12,11 @@ export default Ember.Route.extend({
    },

    willTransition() {
    this._super(...arguments);
    const record = this.controller.get('model');
    if (record.get('isNew')) {
    this.store.unloadRecord(record);
    }
    },
    },
    });
    });
  22. Pablo Brasero revised this gist Apr 11, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -122,7 +122,7 @@ purpose:
    * Implementing `setupController(ctrl, model)` and capturing the
    model there. This is a bit convoluted, and involves two subtle
    risks. The first one is that you may forget calling
    `this.super(ctrl, model)` and wonder why your app doesn't work.
    `this._super(ctrl, model)` and wonder why your app doesn't work.
    The second is that you may then do `this.model = model` and
    overwrite the `model()` function, breaking the route in later
    invocations. There's a similar risk if you overwrite
  23. Pablo Brasero revised this gist Apr 11, 2016. 1 changed file with 29 additions and 8 deletions.
    37 changes: 29 additions & 8 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -96,18 +96,39 @@ this.transitionTo('lines.index');

    ## Accessing the current model

    The correct way to obtain the current model from the route (eg: from an
    action handler) is:
    There seem to be two accepted ways of obtaining the current model
    from a route (eg: from an action handler):

    ```js
    this.controller.get('model')
    // These two should be equivalent
    this.controller.get('model');
    this.modelFor(this.routeName);
    ```

    I have seen other options, but I don't like them:

    * `this.currentModel`: it's a private, undocumented API. Don't
    expect it to be there tomorrow
    * `this.modelFor('foo.bar.baz')`: un-DRY and inconvenient
    I personally prefer the first of the two, for aesthetic reasons
    more than anything. Some people are concerned that "controllers
    will be removed from Ember in the future", but the core team
    reassures us that this is not going to happen any time soon,
    and the replacement code will be very similar. If you want to
    future-proof your application, there are better places to spend
    your time than in avoiding controllers.

    In the wild, you may see other techniques that achieve the same
    purpose:

    * Reading from `this.currentModel`. A private, undocumented API.
    Don't expect it to be there tomorrow

    * Implementing `setupController(ctrl, model)` and capturing the
    model there. This is a bit convoluted, and involves two subtle
    risks. The first one is that you may forget calling
    `this.super(ctrl, model)` and wonder why your app doesn't work.
    The second is that you may then do `this.model = model` and
    overwrite the `model()` function, breaking the route in later
    invocations. There's a similar risk if you overwrite
    `this.currentModel`, but more exciting. Exciting because
    initially it will work but, since it's a private API, who knows
    how it could break in the future.

    ## Trigger `save` on form submit

  24. Pablo Brasero revised this gist Apr 11, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -187,7 +187,7 @@ It's possible to achieve the same effect using `Model#rollbackAttributes`:
    record is not deleted (when it actually is)
    2. It doesn't mirror `Store#createRecord` like `Store#unloadRecord` does

    ### Validation errors on `base`
    ## Validation errors on `base`

    Remember that validation errors can be associated to a record's `base`,
    not only to its properties. You may want to check if there's anything
  25. Pablo Brasero revised this gist Apr 11, 2016. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -10,10 +10,10 @@ and document a clear convention for simple CRUD apps,
    using Ember and Ember Data with no third-party add-ons.

    I hope this will be a useful resource for beginners who,
    fresh out of reading the [Guides](https://guides.emberjs.com/v2.4.0/), could use more directions on
    basic conventions, idioms and patterns in use
    by the Ember community. I also hope to benefit myself
    personally, when readers point out mistakes (which will
    fresh out of reading the [Guides](https://guides.emberjs.com/v2.4.0/),
    could use more directions on basic 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).

  26. Pablo Brasero revised this gist Apr 11, 2016. 1 changed file with 16 additions and 20 deletions.
    36 changes: 16 additions & 20 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -17,6 +17,12 @@ personally, when readers point out mistakes (which will
    be corrected) and bring up differing opinions (which will
    be considered).

    The following sections discuss considerations taken when
    creating this convention. See the final code files listed
    separately further down.

    This is a **work in progress**.

    ## General principles

    This implementation is heavily influenced by the style of
    @@ -37,13 +43,7 @@ This is what I expect from this convention:
    4. The interface must be accomodate for the possibility
    of validation errors

    ## Implementation details

    See the code files listed separately after this README. This
    section describes specific choices made when creating this
    solution, along with rationales.

    ### This is not very DRY
    ## This is not very DRY

    This code could use mixins and components to avoid repetition.
    However I am avoiding this because:
    @@ -54,13 +54,13 @@ However I am avoiding this because:

    If you use this code, you may want to DRY it up as suggested.

    ### The model
    ## The model

    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.

    ### Routes
    ## Routes

    Following Ruby on Rails's lead, the paths/routes for each CRUD
    acion are the following:
    @@ -71,7 +71,7 @@ acion are the following:
    * `/lines/:id/edit` - edit and update a single record
    * `/lines/:id/destroy` - delete a record, but confirm first

    ### The `index` route
    ## The `index` route

    Ember automatically assumes that there is an `index` route when
    we nest routes. Therefore,we **don't** need
    @@ -94,7 +94,7 @@ this.transitionTo('lines');
    this.transitionTo('lines.index');
    ```

    ### Accessing the current model
    ## Accessing the current model

    The correct way to obtain the current model from the route (eg: from an
    action handler) is:
    @@ -109,7 +109,7 @@ I have seen other options, but I don't like them:
    expect it to be there tomorrow
    * `this.modelFor('foo.bar.baz')`: un-DRY and inconvenient

    ### Trigger `save` on form submit
    ## Trigger `save` on form submit

    It's tempting to associate the `save` action with clicking a
    button:
    @@ -132,7 +132,7 @@ to get forms to work as they should:
    This should get you some extra usability and accessibility brownie
    points, if only because that's the way forms are intended to work.

    ### `willTransition`
    ## `willTransition`

    When detecting a move away from the route, prefer `willTransition`
    over `deactivate`. This is because the latter doesn't fire
    @@ -151,7 +151,7 @@ The original record will remain edited (not rolled back), but
    won't have been persisted. After reloading the page, the change
    will disappear.

    ### `unloadRecord`
    ## `unloadRecord`

    To discard an newly created, unsaved record, use `Store#unloadRecord`.
    From the guides, it would appear that `Model#deleteRecord` and
    @@ -172,7 +172,7 @@ behaviour of `deleteRecord` is a bug, acknowledged at https://github.com/emberjs
    Still, I have a preference for `Store#unloadRecord` as it mirrors
    the previous `Store#createRecord`.

    #### Other alternatives
    ### Other alternatives

    It's possible to achieve the same effect using `Model#rollbackAttributes`:

    @@ -186,7 +186,7 @@ It's possible to achieve the same effect using `Model#rollbackAttributes`:
    because is sounds like the attributes are restored (to blank), but the
    record is not deleted (when it actually is)
    2. It doesn't mirror `Store#createRecord` like `Store#unloadRecord` does

    ### Validation errors on `base`

    Remember that validation errors can be associated to a record's `base`,
    @@ -200,7 +200,3 @@ Resources I have found or been pointed to. I'm currently going through them:
    * [A List of Open Source EmberJS Applications](http://iheanyi.com/blog/a-list-of-open-source-emberjs-projects/) by [Iheanyi Ekechukwu](http://iheanyi.com)
    * [Ember.js 2 Tutorial](http://yoember.com/) by [Zoltan Debre](http://zoltan.nz/)
    * [ember-cli-scaffold](https://github.com/marcioj/ember-cli-scaffold) by [Marcio Junior](https://github.com/marcioj)

    ## Please note

    This is a **work in progress**.
  27. pablobm revised this gist Apr 8, 2016. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -36,7 +36,6 @@ This is what I expect from this convention:
    3. Validation will happen server-side
    4. The interface must be accomodate for the possibility
    of validation errors


    ## Implementation details

    @@ -187,6 +186,12 @@ It's possible to achieve the same effect using `Model#rollbackAttributes`:
    because is sounds like the attributes are restored (to blank), but the
    record is not deleted (when it actually is)
    2. It doesn't mirror `Store#createRecord` like `Store#unloadRecord` does

    ### Validation errors on `base`

    Remember that validation errors can be associated to a record's `base`,
    not only to its properties. You may want to check if there's anything
    to display on `model.errors.base`.

    ## See also...

  28. Pablo Brasero revised this gist Apr 8, 2016. 1 changed file with 23 additions and 0 deletions.
    23 changes: 23 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -110,6 +110,29 @@ I have seen other options, but I don't like them:
    expect it to be there tomorrow
    * `this.modelFor('foo.bar.baz')`: un-DRY and inconvenient

    ### Trigger `save` on form submit

    It's tempting to associate the `save` action with clicking a
    button:

    ```html
    <button {{action 'save' model}}>Save</button>
    ```

    But then we cannot press *Enter* on the text field to save. Remember
    that, on the web, data is submitted on a `submit` event. Use this
    to get forms to work as they should:

    ```html
    <form {{action 'save' model on='submit'}}>
    <!-- ...form fields... -->
    <button>Save</button>
    </form>
    ```

    This should get you some extra usability and accessibility brownie
    points, if only because that's the way forms are intended to work.

    ### `willTransition`

    When detecting a move away from the route, prefer `willTransition`
  29. pablobm revised this gist Apr 8, 2016. 2 changed files with 20 additions and 16 deletions.
    18 changes: 10 additions & 8 deletions app..templates..lines..edit.hbs
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,12 @@
    {{#each model.errors.base as |error|}}
    <p class="error">{{error.message}}</p>
    {{/each}}
    <form {{action 'save' model on='submit'}}>
    {{#each model.errors.base as |error|}}
    <p class="error">{{error.message}}</p>
    {{/each}}

    <p>{{input value=model.name placeholder="Name" name="name"}}</p>
    {{#each model.errors.name as |error|}}
    <p class="error">{{error.message}}</p>
    {{/each}}
    <p>{{input value=model.name placeholder="Name" name="name"}}</p>
    {{#each model.errors.name as |error|}}
    <p class="error">{{error.message}}</p>
    {{/each}}

    <p><button {{action 'save' model}}>Update</button> or {{#link-to 'lines'}}Cancel{{/link-to}}</p>
    <p><button>Update</button> or {{#link-to 'lines'}}Cancel{{/link-to}}</p>
    </form>
    18 changes: 10 additions & 8 deletions app..templates..lines..new.hbs
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,12 @@
    {{#each model.errors.base as |error|}}
    <p class="error">{{error.message}}</p>
    {{/each}}
    <form {{action 'save' model on='submit'}}>
    {{#each model.errors.base as |error|}}
    <p class="error">{{error.message}}</p>
    {{/each}}

    <p>{{input value=model.name placeholder="Name" name="name"}}</p>
    {{#each model.errors.name as |error|}}
    <p class="error">{{error.message}}</p>
    {{/each}}
    <p>{{input value=model.name placeholder="Name" name="name"}}</p>
    {{#each model.errors.name as |error|}}
    <p class="error">{{error.message}}</p>
    {{/each}}

    <p><button {{action 'save' model}}>Create</button> or {{#link-to 'lines'}}Cancel{{/link-to}}</p>
    <p><button>Create</button> or {{#link-to 'lines'}}Cancel{{/link-to}}</p>
    </form>
  30. pablobm revised this gist Apr 7, 2016. 1 changed file with 15 additions and 15 deletions.
    30 changes: 15 additions & 15 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -95,6 +95,21 @@ this.transitionTo('lines');
    this.transitionTo('lines.index');
    ```

    ### Accessing the current model

    The correct way to obtain the current model from the route (eg: from an
    action handler) is:

    ```js
    this.controller.get('model')
    ```

    I have seen other options, but I don't like them:

    * `this.currentModel`: it's a private, undocumented API. Don't
    expect it to be there tomorrow
    * `this.modelFor('foo.bar.baz')`: un-DRY and inconvenient

    ### `willTransition`

    When detecting a move away from the route, prefer `willTransition`
    @@ -150,21 +165,6 @@ It's possible to achieve the same effect using `Model#rollbackAttributes`:
    record is not deleted (when it actually is)
    2. It doesn't mirror `Store#createRecord` like `Store#unloadRecord` does

    ### The current model

    The correct way to obtain the current model from the route (eg: from an
    action handler) is:

    ```js
    this.controller.get('model')
    ```

    I have seen other options, but I don't like them:

    * `this.currentModel`: it's a private, undocumented API. Don't
    expect it to be there tomorrow
    * `this.modelFor('foo.bar.baz')`: un-DRY and inconvenient

    ## See also...

    Resources I have found or been pointed to. I'm currently going through them: