AmpersandJS / ampersand-model

Observable objects, for managing state in applications.
MIT License
84 stars 31 forks source link
ampersand ampersand-state ampersand-sync

ampersand-model

Part of the Ampersand.js toolkit for building clientside applications.

ampersand-model is an extension built on ampersand-state to provide methods and properties that you'll often want when modeling data you get from an API.

For further explanation see the learn ampersand-state guide.

Installing

npm install ampersand-model

Observing

Ampersand gets its event system from Backbone using the backbone-events-standalone module on npm.

For more, read all about how events work in ampersand.

Browser compatibility

testling badge

API Reference

The module exports just one item, the ampersand-model constructor. It has a method called extend that works as follows:

extend AmpersandModel.extend({ })

To create a Model class of your own, you extend AmpersandModel and provide instance properties and options for your class. Typically here you will pass any properties (props, session, and derived) of your model class, and any instance methods to be attached to instances of your class.

extend correctly sets up the prototype chain, so that subclasses created with extend can be further extended as many times as you like.

As with AmpersandState, definitions like props, session, derived etc will be merged with superclass definitions.

var Person = AmpersandModel.extend({
    props: {
        firstName: 'string',
        lastName: 'string'
    },
    session: {
        signedIn: ['boolean', true, false],
    },
    derived: {
        fullName: {
            deps: ['firstName', 'lastName'],
            fn: function () {
                return this.firstName + ' ' + this.lastName;
            }
        }
    }
});

constructor/initialize new ExtendedAmpersandModel([attrs], [options])

This works exactly like state with a minor addition: If you pass collection as part of options it'll be stored for reference.

As with AmpersandState, if you have defined an initialize function for your subclass of State, it will be invoked at creation time.

var me = new Person({
    firstName: 'Phil',
    lastName: 'Roberts'
});

me.firstName //=> Phil

Available options:

save model.save([attributes], [options])

Save a model to your database (or alternative persistence layer) by delegating to ampersand-sync. Returns a xhr object if validation is successful and false otherwise. The attributes hash (as in set) should contain the attributes you'd like to change — keys that aren't mentioned won't be altered — but, a complete representation of the resource will be sent to the server. As with set, you may pass individual keys and values instead of a hash. If the model has a validate method, and validation fails, the model will not be saved. If the model isNew, the save will be a "create" (HTTP POST). If the model already exists on the server, the save will be an "update" (HTTP PUT).

If you only want the changed attributes to be sent to the server, call model.save(attrs, {patch: true}). You'll get an HTTP PATCH request to the server with just the passed-in attributes.

Calling save with new attributes will cause a "change" event immediately, a "request" event as the Ajax request begins to go to the server, and a "sync" event after the server has acknowledged the successful change. Pass {wait: true} if you'd like to wait for the server before setting the new attributes on the model.

var book = new AmpersandModel({
  title: "The Rough Riders",
  author: "Theodore Roosevelt"
});

book.save();
//=> triggers a `POST` via ampersand-sync with { "title": "The Rough Riders", "author": "Theodore Roosevelt" }

book.save({author: "Teddy"});
//=> triggers a `PUT` via ampersand-sync with { "title": "The Rough Riders", "author": "Teddy" }

save accepts success and error callbacks in the options hash, which will be passed the arguments (model, response, options). If a server-side validation fails, return a non-200 HTTP response code, along with an error response in text or JSON.

fetch model.fetch([options])

Resets the model's state from the server by delegating a GET to ampersand-sync. Returns a xhr. Useful if the model has yet to be populated with data, or you want to ensure you have the latest server state. A "change" event will be triggered if the retrieved state from the server differs from the current attributes. Accepts success and error callbacks in the options hash, which are both passed (model, response, options) as arguments.

var me = new Person({id: 123});
me.fetch();

destroy model.destroy([options])

Destroys the model on the server by delegating a HTTP DELETE request to ampersand-sync. Returns the xhr object, or false if the model isNew. Accepts success and error callbacks in the options hash, which are both passed (model, response, options) as arguments.

Triggers:

Pass {wait: true} if you'd like to wait for the server to respond before removing the model from the collection.

var task = new Task({id: 123});
task.destroy({
    success: function () {
        alert('Task destroyed!');
    },
    error: function () {
        alert('There was an error destroying the task');
    },
});

sync model.sync(method, model, [options])

Uses ampersand-sync to persist the state of a model to the server. Usually you won't call this directly, you'd use save or destroy instead, but it can be overriden for custom behaviour.

Configuring

ajaxConfig model.ajaxConfig or model.ajaxConfig()

ampersand-sync will call ajaxConfig on your model before it makes the request to the server, and will merge in any options you return to the request. When extending your own model, set an ajaxConfig function to modify the request before it goes to the server.

ajaxConfig can either be an object, or a function that returns an object, with the following options:

var Person = AmpersandModel.extend({
    urlRoot: 'http://otherdomain.example.com/people',

    ajaxConfig: function () {
        return {
            headers: {
                'Access-Token': this.accessToken
            },
            xhrFields: {
                'withCredentials': true
            }
        };
    }
});

var me = new Person({ id: 123 });
me.fetch();

url model.url or model.url()

The relative url that the model should use to edit the resource on the server. By default, url is constructed by sniffing for the model's urlRoot or the model's collection url, if present, then appending the idAttribute if the model has not yet been saved. However, if the model does not follow normal REST endpoint conventions, you may overwrite it. In such a case, url may be absolute.

// overwrite `url()` example
var Person = AmpersandModel.extend({
    props: {
        id: 'number',
        name: 'string'
    },
    url: function() {
        var base = _.result(this, "urlRoot");
        if (this.isNew()) return base;
        return base + '/' + someCustomActionOnServerId(this.getId());
    },
    urlRoot: function() {
        return '/api/' + me.apiVersion + '/persons';
    }
});

var bob = new Person({id: 1234, name: 'bob'});
console.log(bob.urlRoot()); //=> /api/v1/persons
console.log(bob.url()); //=> /api/v1/persons/some/CustomId-bob-1234

urlRoot model.urlRoot or model.urlRoot()

The base url to use for fetching this model. This is useful if the model is not in a collection and you still want to set a fixed "root" but have a dynamic model.url(). Can also be a function.

If your model is in a collection that has a url you won't need this, because the model will try to build the URL from its collection.

var Person = AmpersandModel.extend({
    props: {
        id: 'string',
        name: 'string'
    },
    urlRoot: '/api/persons'
});

var bob = new Person({id: "1234"});

console.log(bob.url()); //=> "/api/persons/1234"

License

MIT