Every repository with this icon (
Every repository with this icon (
Using the built-in REST API
NOTE: This page is currently under construction. Please feel free to make additions. June 24, 2008 -stefanfoulis
You can use SproutCore with any server environment that you can reach using Ajax. Since SproutCore applications run entirely within the web browser, you often only need to write a thin layer of code to actually retrieve data from your server and load it into the in-memory data store. As an alternative to your own custom XHR Requests for fetching data from your backend you can also use the built-in REST API. It defines standard URLs for all the basic CRUD operations.
This allows elegant things like: (assuming contact is a Record instance in the Contacts sample)
contact.refresh();– Refresh the local Store with data from the server.contact.commit();– Save (altered) data to the server.
Creating a new record in the local store and saving it to the backend server can be as simple as:
var newrecord = Contacts.Contact.newRecord({firstName: "Steve",lastName:"Jobs"});– the record will be instantly available in the local store. If there are any widgets on screen that are observing the model the new contact will magically appear on screen.newrecord.dataSource = Contacts.server;– This is a workaround because I have not yet figured out how to do it automatically – stefanfoulisnewrecord.commit();– Will create the record in the backend. Any subsequent.commit()will update the record.
This page will explain how you can use the built in REST API support in your own application. It assumes you can change your web service to fit the conventions assumed by this API. If you cannot make this change, consider writing your own Ajax code instead to load data into the Store.
Preparing the models (Records) in your SproutCore app
In order for the models to be able to know where to GET and POST their changes, some attributes need to be added (contacts/models/contacts.js):
Contacts.Contact = SC.Record.extend( /** @scope Contacts.Contact.prototype */ { // EM: I think this is the correct property name dataSource: Contacts.server, /* A list of all the properties which should be handled by the framework. Additionally to the here mentioned ones, guid and id (?) are added implicitly. */ properties: [‘firstName’,‘lastName’], /* define the URL for this Record type. - updates will be POSTed to ‘/ajaxcom/contact/update’ - new records will be POSTed to ‘/ajaxcom/contact/create’ - and existing records will be fetched (GET) from ‘/ajacom/contact/show/23’ (if the record has guid=23 and only one record is fetched) */ resourceURL: ‘ajaxcom/contact’, fullName: function() { return this.getEach(‘firstName’, ‘lastName’).compact().join(’ ’); }.property(‘firstName’, ‘lastName’)}) ;
Preparing the server backend
NOTE: in order for the requests to work, they have to be served from the same domain.* To make your backend server available to your SproutCore application, use the proxy config statement in your sc-config.rb file. (TODO: add docs on proxy config)
In order for SproutCore to be able to talk to the server backend (e.g Django, RoR…), the backend should always answer with JSON data.
For some instant gratification create a ‘view’ that returns the following JSON at the /ajaxcom/contact/list URL (with a mimetype of “application/json”):
{"records": [ {"last_name": "Jobs", "first_name": "Steve", "guid": "1", "type": "Contact"}, {"last_name": "Sculley", "first_name": "John", "guid": "2", "type": "Contact"}, {"last_name": "Scott", "first_name": "Michael", "guid": "3", "type": "Contact"} ], "ids": [1,2,3] }
Notice that the attributes are defined with underlines instead of camelcase. SproutCore automatically converts the attribute names when talking to the backend. firstName ist converted to first_name.
In the Firebug console this simple command will load the data from the server and insert it into the local Store:
Contacts.server.listFor({recordType: Contacts.Contact})
More coming soon…
Comments
I am not totally sure in that, but i think that the properties, property in the above example could be skipped, since it is added implicitly. So it would look like this:
properties: ['firstName','lastName'],
- aucl
Aucl: you are right. I have fixed this. the properties array was required at one point. -Charles
Charles: My first statement was maybe not totally true, i meant that 'guid' can be avoided, since it is added implicit to the properties. thanks to KVB using any property will just work even without that, but i am not sure (and had no time to verify) if the CRUD operations would also be done correct. (since the framework needs to know what fields to care about and update, commit, …) – aucl
To test this without setting up a whole RESTful server, I created a similar folder structure(ajaxcom/contact) with a file called ‘list’. The contents of this file where identical to the above. To make apache serve this file as ‘application/json’ I also created a ‘.htaccess’ file with the single line ForceType application/json. And don’t forget to change the proxy in sc-config.rb from ‘twitter.com’ to localhost or wherever you server your json file from. – pyth
I have tried creating a data model with and without the ‘properties’ property. The listFor() method works with or without it, but it seams like the commit() method needs to have the properties property otherwise I’m not seeing the data being pushed out to the backend script for create or update — gskluzacek
Shouldn’t the newrecord.dataStore = Contacts.server; above be newrecord.dataSource = Contacts.server; that seems to work for me… -pyth
To pyth’s comment – Yes I believe he is correct about the code newrecord.dataStore = Contacts.server; above should be newrecord.dataSource = Contacts.server;. If you look at records with the SC.Store.records(); statement created from fixtures, or ones created by Contacts.server.listFor({recordType: Contacts.Contact}); you will see they have a dataSource property value of something like SC.Server:99 prefix=[1] _bindings=[0] _observers=[0], but the dataStore property will be null. I have changed the line above to reflect dataSource. — gskluzacek
OK, after digging in the code a bit I have new find regarding the dataSource property, when you are call the newRecord() method you can pass the server property value as the 2nd parameter so the call to newrecord.dataSource = Contacts.server; becomes unnecessary. The call to the newRecord() method would then become var newrecord = Contacts.Contact.newRecord({firstName: "Steve",lastName:"Jobs"}, Contacts.server); — gskluzacek
Maybe the Author can highlight why he sent not only records, but also an array with the id’s? To use this data a custom handler/callback is necessary?! Would be more like for a 2nd part of using REST API. -aucl
It turns out the mentioned JSON layout is obligatory. I have been fighting for a few days now to get it working with a PHP back end. Only when I shaped the data exactly like the example, things started working! The layout has to be
{ "records": [], "ids" [] }
The names of these arrays need to be “records” and “ids” and the arrays themselves need to be unindexed non-associative arrays -Maurits
After having a look at the SC.Server code in the _listSuccess() function it appears that the ids[] array is used to build an array of the actual records added to the store of the recordType passed into the listFor() method… this array is then passed into the function named by the callback option passed into the listFor() method. So if you’re not passing in the callback option, you can just set the ids array in the json encoded string to "ids": []. There is no need to pass any values if you don’t want to. In fact it looks like there is a check to see if the ids[] array even exists, and if it doesn’t then there is no attempt to get the records and build the array. So the "ids": [] can be omitted entirely, but you do still have to include the “records”: array in the returned json string. — gskluzacek







