kjantzer-backbone-child-collections v1.0.0
Backbone Child Collection 1.1.0
Create lazy-loading relations between Backbone.js models and collections with minimal effort. Perfect for REST APIs
Child Collection Example
Let's define and create a model with a collection
var Employees = Backbone.ChildCollection.extend({
// url path will be appended to the url of the parent model
urlPath: 'employees'
})
var Company = Backbone.Model.extend({
urlRoot: '/api/company'
name: 'company', // see below how this is used
// setup all child collections with a key
collections: {
'employees': Employees
}
});
// instantiate a new model
var myCompany = new Company({id: 1, name: 'My Company'});The url for child collections is created automatically using the URL from parent models
console.log( myCompany.get('employees').url() ) // = /api/company/1/employeesThe collections have a reference to the parent model
var employeeColl = myCompany.get('employees');
console.log( employeeColl.parentModel == myCompany ) // trueHere's some more use cases.
// create employee models – POST: /api/company/1/employees
myCompany.get('employees').create([
{id: 1, name: 'John Doe'},
{id: 2, name: 'Jane Doe'}
])
// there's more than one to access children
var firstEmployee = myCompany.get('employees').first()
//var firstEmployee = myCompany.get('employees.first')
//var firstEmployee = myCompany.get('employees.at0')
//var firstEmployee = myCompany.get('employees.1') // ID
// child models inside the child collections can traverse to the parent model
firstEmployee.collection.parentModel == myCompany // true
// of if you give the parent model a `name`, you can do this
firstEmployee.get('company') == myCompany // trueChild Model Example
// Setup a computer model with a link to a single employee model
var Computer = Backbone.Model.extend({
models: {
'employee': {id: 'employee_id', coll: myCompany.get('employees')}
}
});
var computer = new Computer({'employee_id':'1'})
console.log( computer.get('employee') ) // John Doe Model
console.log( computer.get('employee').get('name') ) // "John Doe"Note: a model can have both collections and models defined.
Documentation
Model Attributes and Collection Keys
Model attributes and collection keys should not conflict unless you are wanting to preload the collection with data. If they keys are the same, the child collection with be returned and not the model attribute. However, if the model attribute is an array it will be added to the child collection.
var jsonModelData = {
id: 1,
name: 'My Company',
// this matches the child collection key,
// so when `get(employees)` is used, this data will be converted into a real collection
employees: [
{name: 'Bob'},
{name: 'Jill'}
]
}
// using the example from above...
var myCompany = new Company(jsonModelData);
// returns collection with two employees, Bob and Jill
myCompany.get('employees');Collections setup
You start by putting a list of collections on your model. Multiple structures are supported.
collections: {
'employees': 'employees', // urlPath will be set with generic ChildCollection
'employees': EmployeesColl,
'employees': {
collection: EmployeesColl,
... // any other options listed here will be passed to collection on init
},
'employees': function(){ return EmployeesColl },
'employees': function(){ return {
collection: EmployeesColl,
}}
}As of v0.12.0, child collections can be nested under a group name. This can be helpful when your collections grow in number and you'd like to organize.
// group-coll.js
module.exports = {
'one': Coll1
'two': Coll2
}
// model.js
collections: {
'employees': EmployeesColl,
'group': require('./group-coll')
}
// using
model.get('employees')
model.get('group/one')
model.get('group/two')Models setup
Sometimes you may want to translate a related ID to a real Model. To do this, setup models. Like Collections, you can specify a hash or a function that returns a hash
/* assuming your model looks like:
{
id: 1,
employee_id: 1,
more: 'attrs'
}
*/
models: {
'employee': {
id: 'employee_id', // the attribute on the model,
// where to lookup the id
coll: EmployeesColl // instance
coll: 'EmployeesColl' // string name of instance on this model or global window (useful when instance not defined on load)
coll: function(id, key){ // you can also choose to lookup the model with a custom function instead
return MyLookupCollection.findASpecialModel(id)
},
// optional
fetch: true // if `id` isn't found, it will be fetched from server
}
}
// later: `.get('employee')` == modelModels will also work by having the entire model attributes rather than just and ID. Like so...
/* assuming your model looks like:
{
id: 1,
employee: {
id: 1,
name: 'John Doe',
more: 'attrs'
},
more: 'attrs'
}
*/
models: {
'employee': Employee // will turn the attrs into a real Model
}
// later: `.get('employee')` == modelProperties and methods available
[Collection/Model].parentModel – a reference to the parent model of this collection/model
Model.refColl - a reference to the collection they were fetched from. Only set when a child model is setup with a coll option
Collection.urlPath – the path to be appended to the URL of the parent model.
[Collection/Model].hasFetched (BOOL) – Is set to true after a fetch happens.
[Collection/Model].isFetching (BOOL) – Will be set to true while the fetch method is happening.
Collection.fetchOnce() – Fetches collection if it has not been fetched yet. (Tests for hasFetched)
Collection.timeSinceLastFetch() - Time in miliseconds since last fetched
Collection.stale - if set, fetch will only make request once data is given ms stale.
Collection.fetch({stale:10000}) - Overrides .stale option to signify when the collection data becomes stale. A fetch request will not follow through until the data is stale.
Model.needsFetching
Dot Notation
Dot notation is supported when getting child collections and models.
// collections can return a specific model by providing an index
myCompany.get('employees.at0.name')
// aka
myCompany.get('employees').at(0).get('name')
// `first` and `last` are also supported
myCompany.get('employees.first.name')
myCompany.get('employees.last.name')
// as is model ID
myCompany.get('employees.1.name')A benefit of using dot notation is if a nested item does not exist a fatal error will not occur.
TODO
- Add support for module.exports
Changelog
v1.1.0 - 9/14/18
- child model lookup via
collproperty can be a custom function.
v1.0.0 - 6/26/18
- ready for commonjs loaders such as Webpack
v0.12.0 - 6/25/18
- child
collectionscan be grouped under a common key – helpful for organized modular loading - child models have reference to collection they fetched from via
refColl
v0.11.1
idis not longer required, they key name will be used to find the ID on the model attributes- if model
info.collis a string, it will also check for it on the window
v0.11.0
- dot notation logic changed: index retrieval must be prefixed with "at". ex:
at0. If not, a normalget()will happen - access to parentModel via
namewill traverse all the way to the top (previously limited to first parentModel) - getOrFetch accepts
silentoption - model info defined as a function will be called in context of model
- model info.coll can be a string which will retrieved with dot notation
License
MIT © Kevin Jantzer - Blackstone Publishing