Skip to content

Commit

Permalink
Add load and save events. [Ticket #2531207]
Browse files Browse the repository at this point in the history
  • Loading branch information
rgrove committed Oct 21, 2011
1 parent 9813f48 commit d89a7d2
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 16 deletions.
10 changes: 10 additions & 0 deletions src/app/HISTORY.md
Expand Up @@ -4,6 +4,16 @@ App Framework Change History
3.5.0
-----

### Model

* `load()` now fires a `load` event after the operation completes successfully,
or an `error` event on failure. The `load()` callback (if provided) will still
be called in both cases. [Ticket #2531207]

* `save()` now fires a `save` event after the operation completes successfully,
or an `error` event on failure. The `save()` callback (if provided) will still
be called in both cases. [Ticket #2531207]

### ModelList

* Added a `filter()` method that returns a filtered array of models. [Ticket
Expand Down
86 changes: 78 additions & 8 deletions src/app/docs/model/index.mustache
Expand Up @@ -272,28 +272,28 @@ Model instances provide the following events:
</td>
<td>
<dl>
<dt><strong>changed (<em>Object</em>)</strong></dt>
<dt>`changed` (<em>Object</em>)</dt>
<dd>
<p>
Hash of change information for each attribute that changed. Keys are attribute names, values are objects with the following properties:
</p>

<dl style="margin-top: 1em;">
<dt><strong>`newVal`</strong></dt>
<dt>`newVal`</dt>
<dd>
<p>
The new value of the attribute after it changed.
</p>
</dd>

<dt><strong>`prevVal`</strong></dt>
<dt>`prevVal`</dt>
<dd>
<p>
The old value of the attribute before it changed.
</p>
</dd>

<dt><strong>`src`</strong></dt>
<dt>`src`</dt>
<dd>
<p>
The source of the change, or `null` if no source was specified when the change was made.
Expand All @@ -318,28 +318,42 @@ Model instances provide the following events:
</td>
<td>
<dl>
<dt><strong>error</strong></dt>
<dt>`error`</dt>
<dd>
<p>
Error message, object, or exception generated by the error. Calling `toString()` on this should result in a meaningful error message.
</p>
</dd>

<dt><strong>src</strong></dt>
<dt>`src`</dt>
<dd>
<p>
Source of the error. May be one of the following default sources, or any custom error source used by your Model subclass):
</p>

<dl style="margin-top: 1em;">
<dt><strong>`parse`</strong></dt>
<dt>`load`</dt>
<dd>
<p>
An error loading the model from a sync layer. The sync layer's response (if any) will be provided as the `response` property on the event facade.
</p>
</dd>

<dt>`parse`</dt>
<dd>
<p>
An error parsing a response from a sync layer.
</p>
</dd>

<dt><strong>`validate`</strong></dt>
<dt>`save`</dt>
<dd>
<p>
An error saving the model to a sync layer. The sync layer's response (if any) will be provided as the `response` property on the event facade.
</p>
</dd>

<dt>`validate`</dt>
<dd>
<p>
The model failed to validate.
Expand All @@ -350,6 +364,58 @@ Model instances provide the following events:
</dl>
</td>
</tr>

<tr>
<td>`load`</td>
<td>
<p>
After model attributes are loaded from a sync layer.
</p>
</td>
<td>
<dl>
<dt>`parsed`</dt>
<dd>
<p>
The parsed version of the sync layer's response to the load request.
</p>
</dd>

<dt>`response`</dt>
<dd>
<p>
The sync layer's raw, unparsed response to the load request.
</p>
</dd>
</dl>
</td>
</tr>

<tr>
<td>`save`</td>
<td>
<p>
After model attributes are saved to a sync layer.
</p>
</td>
<td>
<dl>
<dt>`parsed`</dt>
<dd>
<p>
The parsed version of the sync layer's response to the save request.
</p>
</dd>

<dt>`response`</dt>
<dd>
<p>
The sync layer's raw, unparsed response to the save request.
</p>
</dd>
</dl>
</td>
</tr>
</tbody>
</table>

Expand Down Expand Up @@ -659,6 +725,10 @@ pie.save(function (err, response) {
});
```

<p>
In addition to calling the specified callback (if any), the `load()` and `save()` methods will fire a `load` event and a `save` event respectively on success, or an `error` event on failure. See [[#Model Events]] for more details on these events.
</p>

<p>
Always use the `load()` or `save()` methods rather than calling `sync()` directly, since this ensures that the sync layer's response is passed through the `parse()` method and that the model's data is updated if necessary.
</p>
Expand Down
103 changes: 95 additions & 8 deletions src/app/js/model.js
Expand Up @@ -49,12 +49,45 @@ var GlobalEnv = YUI.namespace('Env.Model'),
@param {String} src Source of the error. May be one of the following (or any
custom error source defined by a Model subclass):
* `load`: An error loading the model from a sync layer. The sync layer's
response (if any) will be provided as the `response` property on the
event facade.
* `parse`: An error parsing a JSON response. The response in question will
be provided as the `response` property on the event facade.
* `save`: An error saving the model to a sync layer. The sync layer's
response (if any) will be provided as the `response` property on the
event facade.
* `validate`: The model failed to validate. The attributes being validated
will be provided as the `attributes` property on the event facade.
**/
EVT_ERROR = 'error';
EVT_ERROR = 'error',

/**
Fired after model attributes are loaded from a sync layer.
@event load
@param {Object} parsed The parsed version of the sync layer's response to
the load request.
@param {any} response The sync layer's raw, unparsed response to the load
request.
@since 3.5.0
**/
EVT_LOAD = 'load',

/**
Fired after model attributes are saved to a sync layer.
@event save
@param {Object} [parsed] The parsed version of the sync layer's response to
the save request, if there was a response.
@param {any} [response] The sync layer's raw, unparsed response to the save
request, if there was one.
@since 3.5.0
**/
EVT_SAVE = 'save';

function Model() {
Model.superclass.constructor.apply(this, arguments);
Expand Down Expand Up @@ -299,6 +332,9 @@ Y.Model = Y.extend(Model, Y.Base, {
operation, which is an asynchronous action. Specify a _callback_ function to
be notified of success or failure.
A successful load operation will fire a `load` event, while an unsuccessful
load operation will fire an `error` event with the `src` value "load".
If the load operation succeeds and one or more of the loaded attributes
differ from this model's current attributes, a `change` event will be fired.
Expand All @@ -324,16 +360,41 @@ Y.Model = Y.extend(Model, Y.Base, {
options = {};
}

this.sync('read', options, function (err, response) {
if (!err) {
self.setAttrs(self.parse(response), options);
options || (options = {});

self.sync('read', options, function (err, response) {
var facade = {
options : options,
response: response
},

parsed;

if (err) {
facade.error = err;
facade.src = 'load';

self.fire(EVT_ERROR, facade);
} else {
// Lazy publish.
if (!self._loadEvent) {
self._loadEvent = self.publish(EVT_LOAD, {
preventable: false
});
}

parsed = facade.parsed = self.parse(response);

self.setAttrs(parsed, options);
self.changed = {};

self.fire(EVT_LOAD, facade);
}

callback && callback.apply(null, arguments);
});

return this;
return self;
},

/**
Expand Down Expand Up @@ -378,6 +439,9 @@ Y.Model = Y.extend(Model, Y.Base, {
operation, which is an asynchronous action. Specify a _callback_ function to
be notified of success or failure.
A successful load operation will fire a `load` event, while an unsuccessful
load operation will fire an `error` event with the `src` value "load".
If the save operation succeeds and one or more of the attributes returned in
the server's response differ from this model's current attributes, a
`change` event will be fired.
Expand Down Expand Up @@ -410,13 +474,36 @@ Y.Model = Y.extend(Model, Y.Base, {
return self;
}

options || (options = {});

self.sync(self.isNew() ? 'create' : 'update', options, function (err, response) {
if (!err) {
var facade = {
options : options,
response: response
},

parsed;

if (err) {
facade.error = err;
facade.src = 'save';

self.fire(EVT_ERROR, facade);
} else {
// Lazy publish.
if (!self._loadEvent) {
self._loadEvent = self.publish(EVT_LOAD, {
preventable: false
});
}

if (response) {
self.setAttrs(self.parse(response), options);
parsed = facade.parsed = self.parse(response);
self.setAttrs(parsed, options);
}

self.changed = {};
self.fire(EVT_SAVE, facade);
}

callback && callback.apply(null, arguments);
Expand Down Expand Up @@ -543,7 +630,7 @@ Y.Model = Y.extend(Model, Y.Base, {
@param {Object} [options] Sync options. It's up to the custom sync
implementation to determine what options it supports or requires, if any.
@param {callback} [callback] Called when the sync operation finishes.
@param {Function} [callback] Called when the sync operation finishes.
@param {Error|null} callback.err If an error occurred, this parameter will
contain the error. If the sync operation succeeded, _err_ will be
falsy.
Expand Down

0 comments on commit d89a7d2

Please sign in to comment.