src/DataModel.js
import DataObject from './DataObject.js'
/**
DataModel holds a map of string,value pairs, sometimes fetched from or sent to a back-end server.
It fires events when values are changed so that {Component}s and other logic can react.
*/
export default class extends DataObject {
/**
@param {Object} [data={}]
@param {Object} [options={}]
@param {Object} [fieldDataObjects=null] a map of dataField (string) to DataObject (class), used to create sub-objects in this Model's data
*/
constructor(data = {}, options = {}) {
super(options)
if (typeof this.options.fieldDataObjects === 'undefined') {
this.options.fieldDataObjects = {}
}
this.data = {}
this.collection = null // set or unset by a DataCollection that claims or releases the model
this.setBatch(data)
}
cleanup() {
super.cleanup()
this.data = null
}
has(dataField) {
return typeof this.data[dataField] !== 'undefined'
}
/**
Find a value held within this DataModel.
@return may be native types or, if mapped by options.fieldDataObjects, another DataObject
*/
get(dataField, defaultValue = null) {
if (typeof this.data[dataField] === 'undefined' || this.data[dataField] === null || this.data[dataField] === '') {
return defaultValue
}
return this.data[dataField]
}
/**
Set a key/value pair
*/
set(dataField, value) {
const batch = {}
batch[dataField] = value
return this.setBatch(batch)
}
/**
Set a group of values. The 'values' parameter should be an object that works in for(key in values) loops like a dictionary: {}
If a key is in options.fieldDataObjects then the value will be used to contruct a DataObject and that will be the saved value.
*/
setBatch(values) {
const changes = {}
let changed = false
for (const key in values) {
const result = this._set(key, values[key])
if (result !== DataObject._NO_CHANGE) {
changed = true
changes[key] = result
this.trigger(`changed:${key}`, this, key, result)
}
}
if (changed) {
this.trigger('changed', this, changes)
}
return changes
}
increment(dataField, amount = 1) {
const currentVal = dataField in this.data ? this.data[dataField] : 0
this.set(dataField, currentVal + amount)
}
_set(dataField, data) {
// _set does not fire any events, so you probably want to use set or setBatch
if (data instanceof DataObject) {
if (this.data[dataField] instanceof DataObject) {
this.data[dataField].reset(data.data)
} else {
this.data[dataField] = data
}
} else if (this.options.fieldDataObjects[dataField]) {
if (this.data[dataField]) {
this.data[dataField].reset(data)
} else {
this.data[dataField] = new this.options.fieldDataObjects[dataField](data)
}
} else {
if (this.data[dataField] === data) {
return DataObject._NO_CHANGE
}
if (this.data[dataField] instanceof DataObject) {
this.data[dataField].reset(data)
} else {
this.data[dataField] = data
}
}
return this.data[dataField]
}
delete() {
return new Promise((resolve, reject) => {
super
.delete()
.then((...params) => {
if (this.collection !== null) {
this.collection.remove(this)
}
resolve(...params)
})
.catch((...params) => {
reject(...params)
})
})
}
reset(data = {}) {
for (const key in this.data) {
if (typeof data[key] === 'undefined') {
this.data[key] = null
}
}
this.setBatch(data)
this.trigger('reset', this)
}
equals(obj) {
if (obj === null || typeof obj === 'undefined') return false
if (this === obj) return true
if (typeof obj !== typeof this) return false
if (obj.get('id') === this.get('id')) return true
return false
}
}