ts/model/BaseModel.ts - StructureJS

StructureJS

0.15.2

A class based utility library for building modular and scalable web platform applications. Features opt-in classes and utilities which provide a solid foundation and toolset to build your next project.

File: ts/model/BaseModel.ts

  1. import IBaseModel from '../interface/IBaseModel';
  2. import IBaseModelOptions from '../interface/IBaseModelOptions';
  3. import BaseObject from '../BaseObject';
  4. import Util from '../util/Util';
  5.  
  6. /**
  7. * Base Model is a design pattern used to transfer data between software application subsystems.
  8. *
  9. * Note: If the data doesn't match the property names you can set the value manually after update super method has been called.
  10. * Also in the class you inherit BaseModel from you can override the update method to handle the data how you want.
  11. *
  12. * @class BaseModel
  13. * @extends BaseObject
  14. * @param [data] {any} Provide a way to update the base model upon initialization.
  15. * @param [opts] {{ expand:boolean }} Options for the base model.
  16. * @module StructureJS
  17. * @submodule model
  18. * @requires Extend
  19. * @requires BaseObject
  20. * @requires Util
  21. * @constructor
  22. * @author Robert S. (www.codeBelt.com)
  23. * @example
  24. * // Example how to extend the BaseModel class.
  25. * let data = {
  26. * make: 'Tesla',
  27. * model: 'Model S',
  28. * YeAr: 2014,
  29. * feature: {
  30. * abs: true,
  31. * airbags: true
  32. * }
  33. * }
  34. * let carModel = new CarModel(data);
  35. *
  36. *
  37. * // Example how to extend the BaseModel class.
  38. * class CarModel extends BaseModel {
  39. *
  40. * // You need to have properties so the data will get assigned.
  41. * // If not the data will not get assigned to the model.
  42. * make = null;
  43. * model = null;
  44. * year = null;
  45. * allWheel = false; // Set a default value
  46. *
  47. * // You can assign BaseModel to a property which will
  48. * // automatically created it and pass the data to it.
  49. * feature = FeatureModel
  50. *
  51. * // If you have an array of data and want them assign to a BaseModel.
  52. * feature = [FeatureModel];
  53. *
  54. * constructor(data = {}, opts = {}) {
  55. * super(opts);
  56. *
  57. * if (data) {
  58. * this.update(data);
  59. * }
  60. * }
  61. *
  62. * // @overridden BaseModel.update
  63. * update(data) {
  64. * super.update(data);
  65. *
  66. * // If the data doesn't match the property name.
  67. * // You can set the value(s) manually after the update super method has been called.
  68. * this.year = data.YeAr;
  69. * }
  70. * }
  71. */
  72. class BaseModel extends BaseObject implements IBaseModel
  73. {
  74. /**
  75. * This property helps distinguish a BaseModel from other functions.
  76. *
  77. * @property IS_BASE_MODEL
  78. * @type {boolean}
  79. * @public
  80. * @static
  81. * @readonly
  82. */
  83. public static readonly IS_BASE_MODEL:boolean = true;
  84.  
  85. /**
  86. * @property sjsOptions
  87. * @type {IBaseModelOptions}}
  88. * @public
  89. */
  90. protected sjsOptions:IBaseModelOptions = {
  91. expand: false,
  92. };
  93.  
  94. constructor(opts:IBaseModelOptions = {})
  95. {
  96. super();
  97.  
  98. this.sjsOptions.expand = opts.expand === true;
  99. }
  100.  
  101. /**
  102. * Provide a way to update the Base Model.
  103. *
  104. * @method update
  105. * @param [data={}] {any}
  106. * @public
  107. * @example
  108. * // Example of updating some of the data:
  109. * carModel.update({ year: 2015, allWheel: true});
  110. *
  111. * // Of course you can also do it the following way:
  112. * carModel.year = 2015;
  113. * carModel.allWheel = false;
  114. */
  115. public update(data:any = {}):any
  116. {
  117. Object
  118. .keys(this)
  119. .forEach(propertyName =>
  120. {
  121. // Ignore the sjsId property because it is set in the BaseObject constructor and we don't want to update it.
  122. if (propertyName !== 'sjsId')
  123. {
  124. const propertyData = this[propertyName];
  125. const updateData = data[propertyName];
  126. const dataToUse = (updateData !== void 0) ? updateData : propertyData;
  127.  
  128. this._updatePropertyWithDataPassedIn(propertyName, dataToUse);
  129. }
  130. });
  131.  
  132. return this;
  133. }
  134.  
  135. /**
  136. * Adds the updateData to the property
  137. *
  138. * @method _updatePropertyWithDataPassedIn
  139. * @param propertyName
  140. * @param updateData
  141. * @protected
  142. */
  143. protected _updatePropertyWithDataPassedIn(propertyName:any, updateData:any):void
  144. {
  145. // If the current property on the model is an array and the updateData is an array.
  146. if ((this[propertyName] instanceof Array === true) && (updateData instanceof Array === true))
  147. {
  148. const isPropertyDataValueAnUninstantiatedBaseModel = (typeof this[propertyName][0] === 'function' && this[propertyName][0].IS_BASE_MODEL === true);
  149. const isUpdateDataValueAnUninstantiatedBaseModel = (typeof updateData[0] === 'function' && updateData[0].IS_BASE_MODEL === true);
  150.  
  151. if (isPropertyDataValueAnUninstantiatedBaseModel === false)
  152. {
  153. this[propertyName] = updateData.map(data => this._updateData(null, data));
  154. }
  155. else if (isPropertyDataValueAnUninstantiatedBaseModel === true && isUpdateDataValueAnUninstantiatedBaseModel === false)
  156. {
  157. // If the property data is an uninstantiated BaseModel then we assume the update data passed in
  158. // needs to be create as that BaseModel Class.
  159. const baseModel = this[propertyName][0];
  160. this[propertyName] = updateData.map(data => this._updateData(baseModel, data));
  161. }
  162. else
  163. {
  164. this[propertyName] = [];
  165. }
  166. }
  167. else
  168. {
  169. this[propertyName] = this._updateData(this[propertyName], updateData);
  170. }
  171. }
  172.  
  173. /**
  174. * @method _updateData
  175. * @param propertyData
  176. * @param updateData
  177. * @protected
  178. */
  179. protected _updateData(propertyData:any, updateData:any):any
  180. {
  181. let returnData:any = null;
  182.  
  183. if (this.sjsOptions.expand === false && typeof updateData === 'function' && updateData.IS_BASE_MODEL === true)
  184. {
  185. // If updateData is a function and has an IS_BASE_MODEL static property then it must be a child model and we need to return null
  186. // so it cleans up the BaseModel functions on the property.
  187. // To create empty model(s) pass { expand: true } for the options.
  188. return null;
  189. }
  190.  
  191. if (typeof propertyData === 'function' && propertyData.IS_BASE_MODEL === true && updateData)
  192. {
  193. // If the propertyData is an instance of a BaseModel class and has not been created yet.
  194. // Instantiate it and pass in the updateData to the constructor.
  195. returnData = new propertyData(updateData, this.sjsOptions);
  196. }
  197. else if ((propertyData instanceof BaseModel) === true)
  198. {
  199. // If propertyData is an instance of a BaseModel class and has already been created.
  200. // Call the update method and pass in the updateData.
  201. returnData = propertyData.update(updateData);
  202. }
  203. else if ((updateData instanceof BaseModel) === true)
  204. {
  205. returnData = updateData.clone();
  206. }
  207. else
  208. {
  209. // Else just return the updateData to the property.
  210. returnData = updateData;
  211. }
  212.  
  213. return returnData;
  214. }
  215.  
  216. /**
  217. * Converts the Base Model data into a JSON object and deletes the sjsId property.
  218. *
  219. * @method toJSON
  220. * @returns {any}
  221. * @public
  222. * @example
  223. * const obj = carModel.toJSON();
  224. */
  225. public toJSON():any
  226. {
  227. const clone:any = Util.clone(this);
  228. return Util.deletePropertyFromObject(clone, ['sjsId', 'sjsOptions']);
  229. }
  230.  
  231. /**
  232. * Converts a Base Model to a JSON string,
  233. *
  234. * @method toJSONString
  235. * @returns {string}
  236. * @public
  237. * @example
  238. * const str = carModel.toJSONString();
  239. */
  240. public toJSONString():string
  241. {
  242. return JSON.stringify(this.toJSON());
  243. }
  244.  
  245. /**
  246. * Converts the string json data into an Object and calls the {{#crossLink "BaseModel/update:method"}}{{/crossLink}} method with the converted Object.
  247. *
  248. * @method fromJSON
  249. * @param json {string}
  250. * @public
  251. * @example
  252. * const str = '{"make":"Tesla","model":"Model S","year":2014}'
  253. * const carModel = new CarModel();
  254. * carModel.fromJSON(str);
  255. */
  256. public fromJSON(json:string):any
  257. {
  258. const parsedData:any = JSON.parse(json);
  259.  
  260. this.update(parsedData);
  261.  
  262. return this;
  263. }
  264.  
  265. /**
  266. * Create a clone/copy of the Base Model.
  267. *
  268. * @method clone
  269. * @returns {BaseModel}
  270. * @public
  271. * @example
  272. * const clone = carModel.clone();
  273. */
  274. public clone():BaseModel
  275. {
  276. const clonedBaseModel:BaseModel = new (<any>this).constructor(this);
  277.  
  278. return clonedBaseModel;
  279. }
  280.  
  281. }
  282.  
  283. export default BaseModel;
  284.