mithril-data
v0.4.9
Published
A rich data model library for Mithril javascript framework.
Downloads
21
Readme
mithril-data
A rich data model library for Mithril javascript framework.
Features
- Create brilliant application with Schema-based Model and Collection
- Enriched with Lodash methods, integrated into Model & Collection
- Auto Redraw on Model & Collection changes
- State (View-Model) using Mithril's stream
- Extensible, Configurable and Customizable
- And many more ...
Dependencies
Schema
var userSchema = {
name : 'User',
props : ['name', 'age']
}
var noteSchema = {
name : 'Note',
props : ['title', 'body', 'author'],
defaults : {
title: 'Default Title',
body: 'Default Body'
},
refs : {
author: 'User'
},
url : '/customurl',
redraw : true
}
All available schema options:
- name - (string, required) name of the model
- props - (string array, required) list of model props
- defaults - (object {prop:value}) default value of props
- refs - (object {prop:model}) list of references to other models
- url - (string) the specific url of the model. defaults to model's
name
- redraw - (boolean) trigger a redraw when a model with this schema is updated. defaults to
false
- methods - (object {name:function}) add custom methods to model instances (by schema)
- parser - (function) add data parser to automatically parse data storing to model
Additional option information:
parser - An option to parse different data objects.
// Parsers for Notes schema
parser : function(obj) {
if(obj.kind === '3rd#party') {
return {
title : obj.wrap.title,
body : obj.wrap.inner.body,
author : obj.wrap.inner.author
}
} else {
// Another source
}
}
// This auto parse wrapped data. Also parsed with `setObject()`.
var note = new Note({wrapped: 'data'})
note.setObject({wrapped: 'data'})
// To disable parsing. Set `parse: false` in the options.
var note = new Note({unwrapped: 'data'}, {parse: false})
Model
var User = md.model(userSchema)
var Note = md.model(noteSchema)
var userA = new User()
userA.name('Foo')
userA.age(123)
userA.save(function (err) {
if (!err)
console.log('Saved')
})
var noteA = new Note({
title: 'My Notes'
}, {
redraw : true
})
noteA.body('A note content')
noteA.author(userA)
noteA.save().then(fnResolve, fnReject)
new <ModelConstructor>([initials, options])
Creates an instance of model.
- initials - (object {prop:value}) initial values of props
- options - (object) specific options to model instance
- redraw - (boolean) redraw
- parse - (boolean) set
false
to disable parsing. defaults totrue
#<prop>([value, silent])
Get or set value of prop. If auto-redraw is enabled, pass true
at the end to set without auto redrawing. This uses the basic usage of stream, and to get the stream itself, use <prop>.stream
.
user.name('Foo') // Sets the name to `Foo`
var n = user.name() // Get the name... returns `Foo`
user.name('Bar', true) // Silently sets the name to `Bar` (without redrawing)
var s = user.name.stream.map(callback) // Get the stream object with `<prop>.stream`
#opt(key[, value])
Sets an option(s) specific to the model. See ModelConstructor
for list of options.
#id([strId])
Get or set the ID of model regardless of real ID prop. This is useful if you have different id prop like _id
(mongodb).
// Assumes that you configure `keyId` to `_id`
user._id('Bar') // Sets the id to `Bar`
var id = user.id() // Returns `Bar`
// user.id('Bar') // You can also use this
#lid()
Get the local ID of model. This ID is generated automatically when model is created.
#url()
Get the url. It return the combination of baseUrl
and the models' url
.
#set(key[, value, silent])
Set a value to a prop of the model.
user.set('name', 'Foo') // Sets single prop
// Silent set, will NOT trigger the auto redraw
user.set('age', 34, true) // Pass true at the end
#setObject(obj[, silent])
Set multiple values at once using object.
user.set({name: 'Bar', age: 12}) // Sets multiple props using object
user.set(existingModelInstance) // Sets multiple props user existing model instance
// Silent set, will NOT trigger the auto redraw
user.set({age: 32}, true) // Pass true at the end
#get(key)
Get a value or a copy of all values in object literal format.
user.get('name') // Returns the value of name
user.get() // Returns an object (copy) with all props and values. e.g. {name: "Foo", age: 12}
#getCopy([deep])
Get a copy model in object literal format. Additionally, you can set deep to true
to copy all props recursively.
#attachCollection(collection)
Attach the model to a collection.
#detachCollection(collection)
Detach the model from a collection.
#detach()
Detach the model from ALL associated collections.
#remove()
Triggers detach
and also dispose
the object. Make sure you're not using the model anymore.
#isSaved()
True if it contains id and fresh from store (server or local storage).
#isModified()
True when a prop is modified.
#isDirty()
True if the model is modified or not saved.
#save([options, callback])
Saves the model to data store. To check for result, you can use either callback
or then
. Callback arguments are (err, response, model)
. Properties for options
is the same with m.request
's options but with additional path
string property. path
is the path to actual value for the model in the response object. Like in response:{outer:{model:{}}}
will be "outer.model"
.
user.save()
.then(
function (model) { console.log('Saved') },
function (err) { console.log(err) }
)
.catch(
function(catchErr) { console.log(catchErr) }
)
#fetch([options, callback])
Fetches the model from data store. Model ID required to fetch. This method also accept callback
or then
. Properties for options
is the same with #save()
's options. If placeholder
is given in the configuration, the returned value will be that placeholder string.
user.id('abc123')
user.fetch().then( function (model) { /* Success! model now have other prop values */ } );
// If placeholder is set as 'Loading...'
user.name(); // Returns `Loading...`, until the fetch completed
#destroy([options, callback])
Destroys the model from data store and triggers remove
method. Also accept callback
or then
. Parameter options
is the same with #save()
's options.
#populate()
Populates all references. This will trigger fetch if necessary.
#<lodash methods>()
Model includes few methods of Lodash. has
, keys
, values
, pick
, and omit
. See Lodash for info.
userA.pick(['name', 'age'])
// Returns an object with only two properties `name` and `age`, excluding others.
Collection
var userCollection = new md.Collection({
model : User,
url : '/usercollectionurl',
redraw : true
})
userCollection.add(new User())
new Collection([options])
All available collection options:
- model - (model constructor) the associated model to the collection
- url - (string) the specific url of the collection. defaults to associated model's
name
- redraw - (boolean) trigger a redraw when the collection is updated. Defaults to
false
- state - (State | object | array) set a state factory (View-Model) for the collection. See
#stateOf()
method andmd.State()
for more info.
A collection with redraw =
true
will always trigger aredraw
even though the contained model has redraw =false
.
Omitted
model
in option is allowed and will make the collectiongeneric
. Therefore, some methods will NOT be available, likecreate
andfetch
.
#opt(key[, value])
Sets an option(s) to the collection. See Collection
for list of options.
#add(model[, unshift, silent])
Adds a model to the collection. Optionally, you can add at the beginning with unshift
= true
and silently with silent
= true
.
#addAll(models[, unshift, silent])
Adds an array of models to the collection. Optionally, you can set unshift
and silent
as well.
#create(objects)
Create and add multiple models to the collection from passed array of objects.
userCollection.create([ {name:'Foo'}, {name:'Bar'} ])
#get(mixed)
Get a model from collection. Argument mixed
can be a number
, string
, object
or model
instance. Returns the first matched only otherwise undefined
.
userCollection.get('abc')
userCollection.get(123) // If string or number, it will find by Id
userCollection.get({name:'Foo'}) // Will match the first model with name equal to `Foo`
userCollection.get(model) // Will find by model instance, compared with Lodash's `indexOf`
#getAll(mixedArr[, falsy])
Get multiple models from collection. Array can contain mixed
type, same with get()
. Returns an array of first matched only of each array element. Argument falsy
will include falsy value like undifened
in the list, instead of omitting.
#remove(mixed[, silent])
Removes a model from collection. mixed
can be same with get()
's mixed argument.
#push(model[, silent])
Adds a model or array of models at the end.
#unshift(model[, silent])
Adds a model or array of models at the beginning.
#shift([silent])
Removes the model at the beginning.
#pop([silent])
Removes the model at the end.
#clear([silent])
Removes ALL models.
#sort(props[, orders])
Sort the collection. Argument props
is an array of props to sort and orders
is an array of asc
or desc
. Optioanally, if you're sorting only single prop, you can pass a string instread of array.
// Sort the collection by `name` in default order `asc`
userCollection.sort('name')
// Sort the collection by `name` in `desc` order
userCollection.sort('name', 'desc')
// Sort the collection by `age` first starting from old (desc) and then `name`
userCollection.sort(['age', 'name'], ['desc', 'asc'])
#sortByOrder(order , path )
Sort the collection by giver order.
#pluck()
Pluck a prop from each model.
userCollection.pluck('id')
// Returns [123, 456, 789]
#contains(mixed)
Returns true
if the model contains in the collection, otherwise false
. Argument mixed
is the same with get()
method.
#reserve()
Reverse the order of the collection.
#randomize()
Randomize the order of the collection.
#model()
Get the associated model constructor.
#stateOf(mixed)
Get the state of a model in the collection. Argument mixed
is the same with get()
method.
// Set state signature on creating collection
var col = new md.Collection({
state : {
isEditing: true,
isLoading: false
}
})
// Create user
var user = new User()
// Add user to collection
col.add(user);
// Retrieving state value
col.stateOf(user).isEditing() // Returns `true`
// Setting state
col.stateOf(user).isEditing(false) // Sets and returns `false`
#url()
Get the url.
#fetch(query[, options, callback])
Query to data store and populate the collection. Callback arguments are (err, response, models)
. Properties for options
is the same with m.request
's options but with additional path
and clear
property. path
is the path to actual array of items for the collection in the response object. Like in response:{outer:{items:[]}}
will be "outer.items"
. And clear
will clear the collection before placing the fetched data.
userCollection.fetch({ age : 30 }).then(function (){
// Success! `userCollection` now have models with age 30
})
#hasModel()
Returns true
if the collection has associated model, otherwise false
.
#destroy()
Destroys the collection. Trigger clear
and dispose
.
#isFetching()
Checks if the collection is fetching.
#<lodash methods>()
Collection includes several methods of Lodash. forEach
, map
, find
, findIndex
, findLastIndex
, filter
, reject
, every
, some
, invoke
, maxBy
, minBy
, sortBy
, groupBy
, shuffle
, size
, initial
, without
, indexOf
, lastIndexOf
, difference
, sample
, reverse
, nth
, first
, last
, toArray
, slice
, orderBy
, transform
. See Lodash for info.
var filtered = userCollection.filter({age: 30})
// Returns an array of models with age of 30.
State
Also known as View-Model. See Mithril's view-model description for more info.
var _isEditing = md.stream(false)
// Create state factory
var stateFactory = new md.State({
isLoading: false,
isEditing: _isEditing // Add exisiting stream created somewhere
})
// Creating states
stateFactory.set('A');
stateFactory.set('B');
// Using states
var component = {
controller: function() {
this.stateA = stateFactory.get('A')
this.stateA.isEditing(true)
var s = this.stateA.isEditing.map(callback) // Using with stream
},
view: function(ctrl) {
return m('div', 'Is editing ' + ctrl.stateA.isEditing()) // Displays `Is editing true`
}
}
new State(signature[, options])
Creates a new State factory. signature
can be object or array.
All available state options:
- store - (function) the custom store function (it must return a function). defaults to
stream
that was set inmd.config
- prefix - (string) the string prefix for custom store.
#set(key)
Internally creates a new state by key
.
#get(key)
Get the state by key
.
#remove(key)
Removes the state by key
.
md.State.create(signature[, options])
Creates a state without instantiating new State factory. options
is same with state factory constructor.
// Create state factory
var state = md.State.create({
isWorking: false
})
state.isWorking() // Get state. => false
state.isWorking(true) // Set state. => true
md.State.assign(object, signature[, options])
Same with create
but assigns to given object
, instead of creating new object.
// Existing object
var obj = {};
// Create state factory
md.State.assign(obj, {
isWorking: false
})
obj.isWorking()
Configure & Customize
Configuration must be set before using any of mithril-data
's functionality.
md.config({
baseUrl : '/baseurl',
keyId : '_id', // mongodb
store : customStoreFunction
})
All available config options:
- baseUrl - (string) the base url
- keyId - (string) the custom ID of the model. defaults to
id
- redraw - (boolean) the global redraw flag. default to
false
- cache - (boolean) enable caching the models created by a collection. defaults to
false
- modelMethods - (object { methodName :
function()
}) additional methods to bind tomodel
's prototype - collectionMethods - (object { methodName :
function()
}) additional methods to bind tocollection
's prototype - modelBindMethods - (string array) model's methods to bind to itself. see Lodash
bindAll()
- collectionBindMethods - (string array) collection's methods to bind to itself
- storeConfigOptions - (function) a function to
manipulate the options
before sending data to data-store - storeConfigXHR - (function) a function to
manipulate XHR
before sending data to data-store. see Mithril'sm.request
for more info - storeExtract - (function) a function to trigger after receiving data from data-store. see Mithril's
m.request
for more info - storeSerializer - (function) a function that overrides data-store serializer. see Mithril's
m.request
for more info - storeDeserializer - (function) a function that overrides data-store deserializer. see Mithril's
m.request
for more info - store - (function) a function that handles the storing of data. defaults to
m.request
- stream - (function) a function that handles the model props as well as md's State class. defaults to Mithril's
Stream
- cache - (boolean) should use cache or not in all collections. defaults to
false
- cacheLimit - (number) limit of cache. defaults to
100
- placeholder - (string) the string to return by prop when the model fetching or collection through
isFetching
method
storeConfigOptions
This is useful when you want to modify the options
object before sending to data-store. One scenario is to create custom url instead of default generated url.
user.id('abc123')
user.fetch()
// The default url is `/user?id=abc123` but we want `/user/abc123
md.config({ storeConfigOptions : function (options) {
options.url = options.url + '/' + options.data.id
options.data = null // clear the data as we've used it already
}})
store
A function responsible for storing the data (defaults to m.request
). An example is, if you want to store data using local storage.
var fnLocalStorage = function (data) {
if (data.method === 'POST') { /* Writing data... */ }
else if (data.method === 'GET') { /* Reading data... */ }
else if (data.method === 'DELETE') { /* Deleting data... */ }
else { /* Do something with other methods */ }
}
md.config({ store : fnLocalStorage})
Just make sure that your custom store should return a Promise.
More
md.store
A handy tool that handles request to data-store. The result is through then
/ catch
.
- request(url[, method, data, opt]) - creates a request to data-store. the
opt
will override the options when storing to data-store - get(url[, data, opt]) - calls
request
withGET
method, passing thedata
andopt
- post(url[, data, opt]) - calls
request
withPOST
method, passing thedata
andopt
- destroy(url[, data, opt]) - calls
request
withDELETE
method, passing thedata
andopt
md.stream
Expose Mithril's Stream (unmodified and only bundled).
md.toStream
A helper function to convert any value to stream.
md.model.get(name)
A way to get a model constructor from other scope. Argument name
is the model name.
md.defaultConfig(config)
Overrides the default config.
md.resetConfig()
Resets the config to default. If defaultConfig()
is used, it will reset to that config.
md.noConflict()
Return the old reference to md
.
md.version()
Return the current version.
Installation
# NPM
npm install mithril-data
# Bower
bower install mithril-data
Node / CommonJS:
var md = require('mithril-data');
HTML: (md
is automatically exposed to browser's global scope)
<script type="text/javascript" src="lodash.min.js"></script>
<script type="text/javascript" src="mithril.min.js"></script>
<script type="text/javascript" src="mithril-data.min.js"></script>
<script type="text/javascript">
console.log(md.version());
</script>
License
MIT