3. Custom Ember Data Adapters
• Interface between Ember Data identity mapper
store and some external data store
• Provides methods to find, create, update and
delete records in external data store
• External data store can be anything you like,
REST API, SOAP API, RSS Feed, LocalStorage…
Oli Griffiths - @oligriffiths
4. Custom Ember Data Adapters
//Set the adapter as the application adapter
//Rest API adapter
App.ApplicationAdapter = DS.RestAdapter.extend()
//Fixture (in memory only)
App.ApplicationAdapter = DS.FixtureAdapter.extend()
//Rails active record adapter
App.ApplicationAdapter = DS.ActiveModelAdapter.extend()
//A custom adapter
App.ApplicationAdapter = CustomAdapter.extend()
Oli Griffiths - @oligriffiths
5. Custom Ember Data Adapters
var CustomAdapter = DS.Adapter.extend({
/**
@method find
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {String} id
@return {Promise} promise
*/
find: function(store, type, url) {},
/**
@private
@method findAll
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {String} sinceToken
@return {Promise} promise
*/
findAll: function(store, type) {},
Oli Griffiths - @oligriffiths
6. Custom Ember Data Adapters
/**
@private
@method findQuery
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} query
@param {DS.AdapterPopulatedRecordArray} recordArray
@return {Promise} promise
*/
findQuery: function(store, type, query, array) {},
/**
@method createRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@return {Promise} promise
*/
createRecord: function(store, type, record) {},
Oli Griffiths - @oligriffiths
7. Custom Ember Data Adapters
/**
@method updateRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@return {Promise} promise
*/
updateRecord: function(store, type, record) {},
/**
@method deleteRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@return {Promise} promise
*/
deleteRecord: function(store, type, record) {}
Oli Griffiths - @oligriffiths
9. REST Adapter Example
createRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record, { includeId: true });
return this.ajax(this.buildURL(type.typeKey), "POST", { data: data });
},
updateRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record);
var id = get(record, 'id');
return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data });
},
deleteRecord: function(store, type, record) {
var id = get(record, 'id');
return this.ajax(this.buildURL(type.typeKey, id), "DELETE");
},
Oli Griffiths - @oligriffiths
10. Custom Ember Data Adapters
• Do not store data within the adapter
• Merely transport data from the local data store to
some “external” data store
• Serializers are used to convert the data format
to/from the local object representation to/from
the “external” data store format
Oli Griffiths - @oligriffiths
12. What is a Self Defining App?
• When the full functionality of an app is unknown
at “build time” (when the developer writes the application)
• Additional app functionality is created at runtime
based on external dependencies
• Thus the app “builds” itself whilst running
Oli Griffiths - @oligriffiths
13. Ember: route first mentality
Oli Griffiths - @oligriffiths
PizzaRoute
PizzaController
PizzaModel
PizzaView
14. What if your routes are unknown
at build time?
Oli Griffiths - @oligriffiths
?Route
?Controller
?Model
?View
15. What if your routes are unknown
at build time?
• You have a problem, everything depends on
routes
• Without defined routes, transitioning to a view is
impossible
• Some websites do not have pattern-able routes,
so how can you define routes at “build time”
Oli Griffiths - @oligriffiths
16. So how can we handle this?
• Wild card routes allow you to register a route in
the eventuality that no corresponding route is
found
• Wild card routes are intended to catch when a
user tries to transition to an undefined route and
show an error page
• We can use this functionality to our advantage to
build new routes at runtime
Oli Griffiths - @oligriffiths
17. So how can we handle this?
Oli Griffiths - @oligriffiths
• Here is the flow:
1. In the model hook, create an ember valid route name
based off the URL being transitioned to
2. Make an ajax request to the URL you’re attempting to
transition to (swap .html to .json, or however your API works)
3. Extract an identifying property from the response,
perhaps “type” or “model”
4. Register the URL (path) against the created route name
from (1)
18. So how we can handle this?
5. Register a new route instance in the application
container, manually setting the controller, view
and template name
6. Transition to the newly defined route
Oli Griffiths - @oligriffiths
19. What’s this look like?
App.CatchAllRoute = Ember.Route.extend({
model: function(params, transition)
{
//Get the URL and convert to route name
var url = transition.intent.url;
var route_name = Ember.String.classify(url.replace(/[.|-|/]+/g,'_'));
//Check if route already exists, if so, transition to it
var route = route_name ? this.container.resolve('route:'+route_name) : null;
if(route) return this.transitionTo(route_name);
//Get the data loader and load the data for the destination url
var resolver = this.container.lookup(‘resolver:entity');
//Make an ajax request to the endpoint to get the endpoint data
return resolver.request(url).then(function(data){
//Get the type from the response model property
var type = data.model;
//If no route is set (index) then set the route name to the type
if(!route_name) route_name = type;
Oli Griffiths - @oligriffiths
20. What’s this look like?
//Add a new route for the url in question
App.Router.map(function(){
this.resource(route_name, {path: url});
});
//Register new route, manually setting the controller,template and view names
this.container.register('route:'+route_name, Ember.Route.extend({
controllerName: type,
viewName: type,
templateName: type,
Oli Griffiths - @oligriffiths
!
model: function(){
var plural = type.substr(-1) == 's';
var name = plural ? type.substr(0, type.length-1) : type;
if(!plural) return this.store.find(name, url);
return this.store.filter(name, {url: url}, function(entity){
//filter model data if applicable
}.bind(this));
}
}));
21. What’s this look like?
!
//Transition to new route
return this.transitionTo(route_name);
}.bind(this), function(data){
//Force a manual page change
document.location.href = url;
Oli Griffiths - @oligriffiths
});
! }
})
!
App.ApplicationView = Ember.View.extend({
didInsertElement: function()
{
//Setup click handler to trigger transition
this.$().on('click', 'a', function(e){
var a = $(e.target).closest('a');
//Ensure only links on this domain are captured
if(a.prop('origin') != document.location.origin) return;
//Stop default browser handler
e.preventDefault();
!
//Now you can transition to any route within a view using a links url
this.controller.transitionToRoute(a.prop(‘pathname’));
})
}
})