Skip to content

Commit

Permalink
Merge pull request #4338 from pangratz/doc-reload-backgroundReload
Browse files Browse the repository at this point in the history
[DOC beta] Overhaul documentation for reload / background reload
  • Loading branch information
bmac committed Apr 29, 2016
2 parents 71abed3 + 7d137c3 commit b754321
Show file tree
Hide file tree
Showing 2 changed files with 259 additions and 25 deletions.
181 changes: 163 additions & 18 deletions addon/-private/system/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,14 +417,12 @@ Store = Service.extend({
/**
This method returns a record for a given type and id combination.
The `findRecord` method will always return a **promise** that will be
resolved with the record. If the record was already in the store,
the promise will be resolved immediately. Otherwise, the store
will ask the adapter's `find` method to find the necessary data.
The `findRecord` method will always resolve its promise with the same
object for a given type and `id`.
The `findRecord` method will always return a **promise** that will be
resolved with the record.
Example
```app/routes/post.js
Expand All @@ -437,20 +435,89 @@ Store = Service.extend({
});
```
If you would like to force the record to reload, instead of
loading it from the cache when present you can set `reload: true`
in the options object for `findRecord`.
If the record is not yet available, the store will ask the adapter's `find`
method to find the necessary data. If the record is already present in the
store, it depends on the reload behavior _when_ the returned promise
resolves.
```app/routes/post/edit.js
import Ember from 'ember';
The reload behavior is configured either via the passed `options` hash or
the result of the adapter's `shouldReloadRecord`.
export default Ember.Route.extend({
model: function(params) {
return this.store.findRecord('post', params.post_id, { reload: true });
If `{ reload: true }` is passed or `adapter.shouldReloadRecord` evaluates
to `true`, then the returned promise resolves once the adapter returns
data, regardless if the requested record is already in the store:
```js
store.push({
data: {
id: 1,
type: 'post',
revision: 1
}
});
// adapter#findRecord resolves with
// [
// {
// id: 1,
// type: 'post',
// revision: 2
// }
// ]
store.findRecord('post', 1, { reload: true }).then(function(post) {
post.get("revision"); // 2
});
```
If no reload is indicated via the abovementioned ways, then the promise
immediately resolves with the cached version in the store.
Optionally, if `adapter.shouldBackgroundReloadRecord` evaluates to `true`,
then a background reload is started, which updates the records' data, once
it is available:
```js
// app/adapters/post.js
import ApplicationAdapter from "./application";
export default ApplicationAdapter.extend({
shouldReloadRecord(store, snapshot) {
return false;
},
shouldBackgroundReloadRecord(store, snapshot) {
return true;
}
});
// ...
store.push({
data: {
id: 1,
type: 'post',
revision: 1
}
});
var blogPost = store.findRecord('post', 1).then(function(post) {
post.get('revision'); // 1
});
// later, once adapter#findRecord resolved with
// [
// {
// id: 1,
// type: 'post',
// revision: 2
// }
// ]
blogPost.get('revision'); // 2
```
See [peekRecord](#method_peekRecord) to get the cached version of a record.
@method findRecord
@param {String} modelName
@param {(String|Integer)} id
Expand Down Expand Up @@ -1006,11 +1073,10 @@ Store = Service.extend({
},

/**
`findAll` ask the adapter's `findAll` method to find the records
for the given type, and return a promise that will be resolved
once the server returns the values. The promise will resolve into
all records of this type present in the store, even if the server
only returns a subset of them.
`findAll` ask the adapter's `findAll` method to find the records for the
given type, and returns a promise which will resolve with all records of
this type present in the store, even if the adapter only returns a subset
of them.
```app/routes/authors.js
import Ember from 'ember';
Expand All @@ -1022,6 +1088,85 @@ Store = Service.extend({
});
```
_When_ the returned promise resolves depends on the reload behavior,
configured via the passed `options` hash and the result of the adapter's
`shouldReloadAll` method.
If `{ reload: true }` is passed or `adapter.shouldReloadAll` evaluates to
`true`, then the returned promise resolves once the adapter returns data,
regardless if there are already records in the store:
```js
store.push({
data: {
id: 'first',
type: 'author'
}
});
// adapter#findAll resolves with
// [
// {
// id: 'second',
// type: 'author'
// }
// ]
store.findAll('author', { reload: true }).then(function(authors) {
authors.getEach("id"); // ['first', 'second']
});
```
If no reload is indicated via the abovementioned ways, then the promise
immediately resolves with all the records currently loaded in the store.
Optionally, if `adapter.shouldBackgroundReloadAll` evaluates to `true`,
then a background reload is started. Once this resolves, the array with
which the promise resolves, is updated automatically so it contains all the
records in the store:
```js
// app/adapters/application.js
export default DS.Adapter.extend({
shouldReloadAll(store, snapshotsArray) {
return false;
},
shouldBackgroundReloadAll(store, snapshotsArray) {
return true;
}
});
// ...
store.push({
data: {
id: 'first',
type: 'author'
}
});
var allAuthors;
store.findAll('author').then(function(authors) {
authors.getEach('id'); // ['first']
allAuthors = authors;
});
// later, once adapter#findAll resolved with
// [
// {
// id: 'second',
// type: 'author'
// }
// ]
allAuthors.getEach('id'); // ['first', 'second']
```
See [peekAll](#method_peekAll) to get an array of current records in the
store, without waiting until a reload is finished.
See [query](#method_query) to only get a subset of records from the server.
@method findAll
@param {String} modelName
@param {Object} options
Expand Down
103 changes: 96 additions & 7 deletions addon/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,10 +465,34 @@ export default Ember.Object.extend({
reload a record from the adapter when a record is requested by
`store.findRecord`.
If this method returns true, the store will re-fetch a record from
the adapter. If this method returns false, the store will resolve
If this method returns `true`, the store will re-fetch a record from
the adapter. If this method returns `false`, the store will resolve
immediately using the cached record.
For example, if you are building an events ticketing system, in which users
can only reserve tickets for 20 minutes at a time, and want to ensure that
in each route you have data that is no more than 20 minutes old you could
write:
```javascript
shouldReloadRecord: function(store, ticketSnapshot) {
var timeDiff = moment().diff(ticketSnapshot.attr('lastAccessedAt')).minutes();
if (timeDiff > 20) {
return true;
} else {
return false;
}
}
```
This method would ensure that whenever you do `store.findRecord('ticket',
id)` you will always get a ticket that is no more than 20 minutes old. In
case the cached version is more than 20 minutes old, `findRecord` will not
resolve until you fetched the latest version.
By default this hook returns `false`, as most UIs should not block user
interactions while waiting on data update.
@method shouldReloadRecord
@param {DS.Store} store
@param {DS.Snapshot} snapshot
Expand All @@ -483,9 +507,38 @@ export default Ember.Object.extend({
reload all records from the adapter when records are requested by
`store.findAll`.
If this method returns true, the store will re-fetch all records from
the adapter. If this method returns false, the store will resolve
immediately using the cached record.
If this method returns `true`, the store will re-fetch all records from
the adapter. If this method returns `false`, the store will resolve
immediately using the cached records.
For example, if you are building an events ticketing system, in which users
can only reserve tickets for 20 minutes at a time, and want to ensure that
in each route you have data that is no more than 20 minutes old you could
write:
```javascript
shouldReloadAll: function(store, snapshotArray) {
var snapshots = snapshotArray.snapshots();
return snapshots.any(function(ticketSnapshot) {
var timeDiff = moment().diff(ticketSnapshot.attr('lastAccessedAt')).minutes();
if (timeDiff > 20) {
return true;
} else {
return false;
}
});
}
```
This method would ensure that whenever you do `store.findAll('ticket')` you
will always get a list of tickets that are no more than 20 minutes old. In
case a cached version is more than 20 minutes old, `findAll` will not
resolve until you fetched the latest versions.
By default this methods returns `true` if the passed `snapshotRecordArray`
is empty (meaning that there are no records locally available yet),
otherwise it returns `false`.
@method shouldReloadAll
@param {DS.Store} store
Expand All @@ -504,9 +557,27 @@ export default Ember.Object.extend({
This method is *only* checked by the store when the store is
returning a cached record.
If this method returns true the store will re-fetch a record from
If this method returns `true` the store will re-fetch a record from
the adapter.
For example, if you do not want to fetch complex data over a mobile
connection, or if the network is down, you can implement
`shouldBackgroundReloadRecord` as follows:
```javascript
shouldBackgroundReloadRecord: function(store, snapshot) {
var connection = window.navigator.connection;
if (connection === 'cellular' || connection === 'none') {
return false;
} else {
return true;
}
}
```
By default this hook returns `true` so the data for the record is updated
in the background.
@method shouldBackgroundReloadRecord
@param {DS.Store} store
@param {DS.Snapshot} snapshot
Expand All @@ -524,9 +595,27 @@ export default Ember.Object.extend({
This method is *only* checked by the store when the store is
returning a cached record array.
If this method returns true the store will re-fetch all records
If this method returns `true` the store will re-fetch all records
from the adapter.
For example, if you do not want to fetch complex data over a mobile
connection, or if the network is down, you can implement
`shouldBackgroundReloadAll` as follows:
```javascript
shouldBackgroundReloadAll: function(store, snapshotArray) {
var connection = window.navigator.connection;
if (connection === 'cellular' || connection === 'none') {
return false;
} else {
return true;
}
}
```
By default this method returns `true`, indicating that a background reload
should always be triggered.
@method shouldBackgroundReloadAll
@param {DS.Store} store
@param {DS.SnapshotRecordArray} snapshotRecordArray
Expand Down

0 comments on commit b754321

Please sign in to comment.