StructureJS
0.15.2A 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.
import IBaseModel from '../interface/IBaseModel'; import IBaseModelOptions from '../interface/IBaseModelOptions'; import BaseObject from '../BaseObject'; import Util from '../util/Util'; /** * Base Model is a design pattern used to transfer data between software application subsystems. * * Note: If the data doesn't match the property names you can set the value manually after update super method has been called. * Also in the class you inherit BaseModel from you can override the update method to handle the data how you want. * * @class BaseModel * @extends BaseObject * @param [data] {any} Provide a way to update the base model upon initialization. * @param [opts] {{ expand:boolean }} Options for the base model. * @module StructureJS * @submodule model * @requires Extend * @requires BaseObject * @requires Util * @constructor * @author Robert S. (www.codeBelt.com) * @example * // Example how to extend the BaseModel class. * let data = { * make: 'Tesla', * model: 'Model S', * YeAr: 2014, * feature: { * abs: true, * airbags: true * } * } * let carModel = new CarModel(data); * * * // Example how to extend the BaseModel class. * class CarModel extends BaseModel { * * // You need to have properties so the data will get assigned. * // If not the data will not get assigned to the model. * make = null; * model = null; * year = null; * allWheel = false; // Set a default value * * // You can assign BaseModel to a property which will * // automatically created it and pass the data to it. * feature = FeatureModel * * // If you have an array of data and want them assign to a BaseModel. * feature = [FeatureModel]; * * constructor(data = {}, opts = {}) { * super(opts); * * if (data) { * this.update(data); * } * } * * // @overridden BaseModel.update * update(data) { * super.update(data); * * // If the data doesn't match the property name. * // You can set the value(s) manually after the update super method has been called. * this.year = data.YeAr; * } * } */ class BaseModel extends BaseObject implements IBaseModel { /** * This property helps distinguish a BaseModel from other functions. * * @property IS_BASE_MODEL * @type {boolean} * @public * @static * @readonly */ public static readonly IS_BASE_MODEL:boolean = true; /** * @property sjsOptions * @type {IBaseModelOptions}} * @public */ protected sjsOptions:IBaseModelOptions = { expand: false, }; constructor(opts:IBaseModelOptions = {}) { super(); this.sjsOptions.expand = opts.expand === true; } /** * Provide a way to update the Base Model. * * @method update * @param [data={}] {any} * @public * @example * // Example of updating some of the data: * carModel.update({ year: 2015, allWheel: true}); * * // Of course you can also do it the following way: * carModel.year = 2015; * carModel.allWheel = false; */ public update(data:any = {}):any { Object .keys(this) .forEach(propertyName => { // Ignore the sjsId property because it is set in the BaseObject constructor and we don't want to update it. if (propertyName !== 'sjsId') { const propertyData = this[propertyName]; const updateData = data[propertyName]; const dataToUse = (updateData !== void 0) ? updateData : propertyData; this._updatePropertyWithDataPassedIn(propertyName, dataToUse); } }); return this; } /** * Adds the updateData to the property * * @method _updatePropertyWithDataPassedIn * @param propertyName * @param updateData * @protected */ protected _updatePropertyWithDataPassedIn(propertyName:any, updateData:any):void { // If the current property on the model is an array and the updateData is an array. if ((this[propertyName] instanceof Array === true) && (updateData instanceof Array === true)) { const isPropertyDataValueAnUninstantiatedBaseModel = (typeof this[propertyName][0] === 'function' && this[propertyName][0].IS_BASE_MODEL === true); const isUpdateDataValueAnUninstantiatedBaseModel = (typeof updateData[0] === 'function' && updateData[0].IS_BASE_MODEL === true); if (isPropertyDataValueAnUninstantiatedBaseModel === false) { this[propertyName] = updateData.map(data => this._updateData(null, data)); } else if (isPropertyDataValueAnUninstantiatedBaseModel === true && isUpdateDataValueAnUninstantiatedBaseModel === false) { // If the property data is an uninstantiated BaseModel then we assume the update data passed in // needs to be create as that BaseModel Class. const baseModel = this[propertyName][0]; this[propertyName] = updateData.map(data => this._updateData(baseModel, data)); } else { this[propertyName] = []; } } else { this[propertyName] = this._updateData(this[propertyName], updateData); } } /** * @method _updateData * @param propertyData * @param updateData * @protected */ protected _updateData(propertyData:any, updateData:any):any { let returnData:any = null; if (this.sjsOptions.expand === false && typeof updateData === 'function' && updateData.IS_BASE_MODEL === true) { // 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 // so it cleans up the BaseModel functions on the property. // To create empty model(s) pass { expand: true } for the options. return null; } if (typeof propertyData === 'function' && propertyData.IS_BASE_MODEL === true && updateData) { // If the propertyData is an instance of a BaseModel class and has not been created yet. // Instantiate it and pass in the updateData to the constructor. returnData = new propertyData(updateData, this.sjsOptions); } else if ((propertyData instanceof BaseModel) === true) { // If propertyData is an instance of a BaseModel class and has already been created. // Call the update method and pass in the updateData. returnData = propertyData.update(updateData); } else if ((updateData instanceof BaseModel) === true) { returnData = updateData.clone(); } else { // Else just return the updateData to the property. returnData = updateData; } return returnData; } /** * Converts the Base Model data into a JSON object and deletes the sjsId property. * * @method toJSON * @returns {any} * @public * @example * const obj = carModel.toJSON(); */ public toJSON():any { const clone:any = Util.clone(this); return Util.deletePropertyFromObject(clone, ['sjsId', 'sjsOptions']); } /** * Converts a Base Model to a JSON string, * * @method toJSONString * @returns {string} * @public * @example * const str = carModel.toJSONString(); */ public toJSONString():string { return JSON.stringify(this.toJSON()); } /** * Converts the string json data into an Object and calls the {{#crossLink "BaseModel/update:method"}}{{/crossLink}} method with the converted Object. * * @method fromJSON * @param json {string} * @public * @example * const str = '{"make":"Tesla","model":"Model S","year":2014}' * const carModel = new CarModel(); * carModel.fromJSON(str); */ public fromJSON(json:string):any { const parsedData:any = JSON.parse(json); this.update(parsedData); return this; } /** * Create a clone/copy of the Base Model. * * @method clone * @returns {BaseModel} * @public * @example * const clone = carModel.clone(); */ public clone():BaseModel { const clonedBaseModel:BaseModel = new (<any>this).constructor(this); return clonedBaseModel; } } export default BaseModel;