Backbone + Titanium - part 2
//Customise Backbone.sync to work with Titanium rather than jQuery | |
var getUrl = function(object) { | |
if (!(object && object.url)) return null; | |
return _.isFunction(object.url) ? object.url() : object.url; | |
}; | |
Backbone.sync = (function() { | |
var methodMap = { | |
'create': 'POST', | |
'read' : 'GET', | |
'update': 'PUT', | |
'delete': 'DELETE' | |
}; | |
return function(method, model, options) { | |
var xhr = Ti.Network.createHTTPClient({ timeout: 35000 }); | |
var type = methodMap[method], | |
params = _.extend({}, options); | |
//==== Start standard Backbone.sync code ==== | |
// Ensure that we have a URL | |
if (!params.url) params.url = getUrl(model) || urlError(); | |
// Ensure that we have the appropriate request data. | |
if (!params.data && model && (method == 'create' || method == 'update')) { | |
params.data = model.toParams(); | |
} | |
// For older servers, emulate JSON by encoding the request into an HTML-form. | |
if (Backbone.emulateJSON) { | |
params.contentType = 'application/x-www-form-urlencoded'; | |
params.processData = true; | |
params.data = params.data ? {model : params.data} : {}; | |
} | |
// For older servers, emulate HTTP by mimicking the HTTP method with `_method` | |
// And an `X-HTTP-Method-Override` header. | |
if (Backbone.emulateHTTP) { | |
if (type === 'PUT' || type === 'DELETE') { | |
if (Backbone.emulateJSON) params.data._method = type; | |
params.type = 'POST'; | |
params.beforeSend = function(xhr) { | |
xhr.setRequestHeader('X-HTTP-Method-Override', type); | |
}; | |
} | |
} | |
//==== End standard Backbone.sync code ==== | |
//Handle success | |
xhr.onload = function() { | |
var cookies = this.getResponseHeader('Set-Cookie'); | |
if(cookies != null && Ti.Android) | |
Ti.App.Properties.setString('cookies', cookies); | |
params.success(JSON.parse(this.responseText)); | |
}; | |
//Handle error | |
xhr.onerror = params.error; | |
//Prepare the request | |
xhr.open(type, params.url); | |
//Add request headers etc. | |
if(params.contentType) | |
xhr.setRequestHeader('Content-Type', params.contentType); | |
xhr.setRequestHeader('Accept', 'application/json'); | |
if (params.beforeSend) params.beforeSend(xhr); | |
if(Ti.Android && Ti.App.Properties.getString('cookies')) | |
xhr.setRequestHeader('Cookie', Ti.App.Properties.getString('cookies')); | |
//Make the request | |
xhr.send(params.data); | |
}; | |
})(); |
class Post extends Backbone.Model | |
urlRoot: app.endpoint + "/posts" | |
toParams: -> | |
# assuming you've stored an instance of the previous QueryStringBuilder class... | |
app.query_string_builder.stringify(this.attributes, "post") | |
class Posts extends Backbone.Collection | |
url: app.endpoint + "/posts" | |
model: Post | |
app.models.Post = Post | |
app.models.Posts = Posts |
class PostViewController | |
initialize: (@post) -> | |
# create @window and @tableView | |
data = [] | |
data.push @createTitleRow() | |
data.push @createBodyRow() | |
data.push @createAuthorRow() | |
@tableView.data = data | |
createTitleRow: -> | |
row = Ti.UI.createTableViewRow | |
title: @post.get('title') | |
@post.bind 'change:title', (e) => | |
row.title = @post.get('title') | |
row | |
# ... |
class PostsViewController | |
# assumming an existing @tableView and @window | |
reload: -> | |
@posts = new app.models.Posts() | |
@posts.fetch | |
success: => | |
@prepareData(@posts.models) | |
error: (e) => | |
Ti.API.error JSON.stringify(e) | |
prepareData: (models) -> | |
@tableView.data = _.map models, (model) -> | |
{ title: model.get('title'), model: model, hasChild: true } |
class QueryStringBuilder | |
stringify: (obj, prefix, accum = {}) -> | |
if _.isArray(obj) | |
this.stringifyArray obj, prefix, accum | |
else if _.isString(obj) || _.isNumber(obj) || _.isDate(obj) || "#{obj}" == "[object TiBlob]" | |
this.stringifyString obj, prefix, accum | |
else if _.isBoolean(obj) | |
this.stringifyBoolean obj, prefix, accum | |
else if obj? | |
if obj.attributes? | |
this.stringifyObject obj.attributes, prefix, accum | |
else | |
this.stringifyObject obj, prefix, accum | |
else | |
return prefix | |
accum | |
stringifyBoolean: (bool, prefix, accum) -> | |
unless prefix | |
throw new TypeError("Stringify expects an object") | |
accum[prefix] = if bool then 1 else 0 | |
stringifyString: (str, prefix, accum) -> | |
unless prefix | |
throw new TypeError("Stringify expects an object") | |
accum[prefix] = str | |
stringifyArray: (arr, prefix, accum) -> | |
unless prefix | |
throw new TypeError("Stringify expects an object") | |
i = 0 | |
for item in arr | |
this.stringify(item, "#{prefix}[#{i++}]", accum) | |
stringifyObject: (obj, prefix, accum) -> | |
for key, value of obj | |
continue if key.match(/_preview$/) | |
new_key = key | |
if _.isArray(value) | |
new_key = "#{key}_attributes" | |
new_prefix = '' | |
if prefix | |
new_prefix = "#{prefix}[#{encodeURIComponent(new_key)}]" | |
else | |
new_prefix = encodeURIComponent new_key | |
this.stringify value, new_prefix, accum |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment