{"id":21417911,"url":"https://github.com/msg-systems/datamodeljs","last_synced_at":"2025-07-14T05:30:33.390Z","repository":{"id":57153384,"uuid":"45982971","full_name":"msg-systems/datamodeljs","owner":"msg-systems","description":"Data Model Management Library for JavaScript","archived":false,"fork":false,"pushed_at":"2016-09-16T09:24:17.000Z","size":2921,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-11-10T03:16:04.253Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/msg-systems.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-11-11T13:20:44.000Z","updated_at":"2017-11-12T20:37:43.000Z","dependencies_parsed_at":"2022-09-07T06:11:24.170Z","dependency_job_id":null,"html_url":"https://github.com/msg-systems/datamodeljs","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msg-systems%2Fdatamodeljs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msg-systems%2Fdatamodeljs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msg-systems%2Fdatamodeljs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msg-systems%2Fdatamodeljs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/msg-systems","download_url":"https://codeload.github.com/msg-systems/datamodeljs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225950756,"owners_count":17550318,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-22T19:18:06.549Z","updated_at":"2024-11-22T19:18:07.233Z","avatar_url":"https://github.com/msg-systems.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# datamodeljs\nData Model Management Library for JavaScript\n\n\n\u003e __The main goal of *datamodeljs* is to ensure a consistent and reliable data structure when communicating with a backend service.__\n\nIn all projects the data structure between backend and frontend can be considered as an interface contract. If either side changes the structure of the data - the counterpart has problems validating and handling the data.\n \nIn order to avoid long lasting debugging sessions in a JavaScript application we use *datamodeljs* to read in and validate the delivered data against the defined entity model classes.\n\n\n\u003e __A secondary goal of *datamodeljs* is the ability to manage entity objects and keep track of their states__ (e.g. newly created, marked as changed, marked for deletion)\n\nThis entity object management allows the JavaScript application to manipulate business objects on block before sending them anywhere.\n\nIn order to be able to handle complex business objects *datamodeljs* implements a bi-directional entity model. Examples can be seen in \"Defining entity classes\"\n\nThe [Github page](http://msg-systems.github.io/datamodeljs/) contains an interactive example and demonstrates the possibilities with *datamodeljs*. \n\n# Entity object states\n\nEntities created with *datamodeljs* know their entity class name:\n\n1. `_className` - the string representation of the entities class name (as declared with the `define` function)\n\nEntities created with *datamodeljs* also can track four different states:\n\n1. `_isDirty` -   has the entity changed on client side since it load time\n2. `_isDeleted` - is the entity marked for deletion on client side \n3. `_isTransient` - was the entity created newly on client side\n4. `_isStub` - is the entity referenced by another entities relationship, but the entity itself was not loaded by now\t  \n\nIn most cases a dirty entity leads to an PUT call in the backend. A deleted entity leads a DELETE call. A transient entity results in a POST call and a stub entity should be loaded using a GET call. Mapping this is not part of *datamodeljs* since it has not the goal to cover the AJAX requests. Mapping the entity states to proper AJAX requests is the applications task.\n\n# Entity serialisation and cloning\n\nWhen serialising or cloning entity objects their internal object properties `_className`, `_isDirty`, `_isDeleted`, `_isTransient` and `_isStub` are not exposed. So using JSON.stringify before sending entites to a service ensures that no internal property is send to the service. Also cloning entity objects simply creates a new JavaScript object that has the entites properties and values set - but it is not considered an entity object anymore since the internal object properties are not cloned.\n\n# Initialization\n\n*datamodeljs* exposes itself to the global variable '`datamodeljs`'. In case that a different symbol for datamodeljs is needed use the `symbol` function. This function allows you to occupy a different global name by giving a new name or releasing the last name by providing no name or an empty name.\n\n\n        datamodeljs.symbol([symbol:String]) : datamodeljs\n         \nExample:\n```js\n    // define an alternate global exposure \n    datamodeljs.symbol(\"dmjs\")\n    // delete the former alternate global exposure and restore the old value of 'dmjs'\n    datamodeljs.symbol(\"\")\n    datamodeljs.symbol()\n```\n\n*datamodeljs* allows the definition of different dataManager storages by using the `dm` function:\n\n        datamodeljs.dm(name:String) : dataManager\n\nExample:\n```js\n    var myDataManager = datamodeljs.dm(\"mine\");\n    // all further examples are working with this dataManager\n    var dm = datamodeljs.dm(\"default\")\n```\n\n# Data management\n\nThe `dataManager` created by *datamodeljs* enables the entity class definition and entity management. The following chapters describe the possibilities in detail:\n\n## Defining entity classes\n\nDefining an entity class is the basic for working with entities. Entities are defined with the `define` function. A class definition needs at least an unique class name (`cls`) and its specification (`spec`). A parent class name is optional. If a parent class name is given, all attributes of that class will be copied into the new class name. Please be aware that this is no real extends mechanism of those classes. \n\n        dataManager.define(cls:String, [parentCls:String], spec:Object) : void\n\nThe class specification is a list of attributes following this syntax:\n\n    \t{\n        \t(attribute: (primary)?type(arity)?  )*\n    \t};\n    \n\n1. The attribute's name is set by the `attribute` tag. \nThe internal properties  `_className`, `_isDirty`, `_isDeleted`, `_isTransient` and `_isStub` are reserved and can not be used as an class attribute. \n2. One attribute can be defined as `primary` field with the prefix `@`. \n3. The attribute's `type` can be either `number`, `boolean`, `string`, `object` or any other class name \n4. The `arity` defines this attributes relation as an arrayed relation and it can have different options\n\t1. `?` - stands for zero or one(0..1) elements of the given `type`\n\t2. `+` - stands for one or more(1..n) elements of the given `type`\n\t3. `*` - stands for zero or more(0..n) elements of the given `type`\n\nExamples:\n```js\n    dm.define(\"AbstractData\", {\n        id: '@number',                          // this is the primary field\n        version: 'number'\n    });\n    \n    dm.define(\"Verweis\", \"AbstractData\", {      // get all attributes of AbstractData copied\n        verweisTyp: \"string\", \n        anzeigename: \"string\",\n        knoten: \"Knoten\"                        // relation to objects of class Knoten\n    });\n    \n    dm.define(\"Knoten\", \"AbstractData\", {\n        anzeigename: 'string',\n        kindKnoten: 'Knoten*',                  // Knoten can have n childs being Knoten as well\n        pfad: 'string'\n    });\n```\n\n## Undefining entity classes\n\nRemoving a class definition from the `dataManager` is possible with the `undefine` function:\n\n        dataManager.undefine(cls:String) : void\n\nUsing this function is removing the class, its specification and all entity objects from the `dataManager`. It is most useful when data models are loaded dynamically. \n    \n## Creating entity objects\n\nCreating objects of entities is available with the `create` function.\n\n        dataManager.create(cls:String, payload:Object) : entity\n\n1. The `cls` must be one of the defined class names within the `dataManager` \n2. The `payload` is the JSON object that should be converted into an entity. The given payload is hereby validated against the entity class specification\n\nExample:\n```js \n    dm.create(\"Verweis\", {\n\t    id: 1,\n\t\tversion: 1,\n        verweisTyp: \"Symbol\",\n\t\tanzeigename: \"symbolic link\",\n\t\tknoten: 1\t\t\t\t\t\t// it is sufficient to only provide the id of the related object\n \t\t\t\t\t\t\t\t\t\t// the dataManager resolves the relation using the given unique id\n    })\n```\n\nThe given example creates two entity objects. The first is the desired `Verweis`. The second object is a `Knoten` with `id` set to  but the flag __stub__ set to `true`. Meaning that this object is referenced somewhere but it was not yet loaded. If this objects get loaded and created later on, then the reference to this `Knoten` wont change, only its content will be updated and its __stub__ flag will be set to `false`.\n\nExample:\n```js\n    dm.create(\"Knoten\", {\n\t    id: 1,\n\t\tversion: 1,\n\t\tanzeigename: \"Root Knoten\",\n\t\tkindKnoten: [],\t\t\t\t\t// it is ok if this array is empty since the arity is *\n\t\tpfad: \"/\" \n    })\n```\n\n## Deleting entity objects\n\nDeleting a single entity object is possible using the `destroy` function.\n\n        dataManager.destroy(cls:String, obj:Object, [force:Boolean]) : void\n\nThe object will be immediatly removed from the `dataManager` if it was marked as `transient` entity or if `force` is set to `true`. Otherwise it will be marked as `deleted`. This way the JavaScript application can still handle the deletion with a proper AJAX call.\n\nExample: \n```js\n    dm.destroy(\"Verweis\", dm.findById(\"Verweis\", 1))\n```\n----------\n\nThe deletion of all objects of a given entity class(`cls`) is possible using the `destroyAll` function.\n \n        dataManager.destroyAll(cls:String) : void\n\nExample: \n```js\n    dm.destroyAll(\"Knoten\")\n```\n\n## Tracking and changing entity object states\n\nChanging the states of an entity is the JavaScript application's task. Entity attributes can be altered as usual in JavaScript applications. The entity class name `_className` can not be changed or deleted from the entity object. The specific entity states `_isDirty`, `_isDeleted`, `_isTransient` and `_isStub` can only be altered to a new legal boolean value. \n\n\t\t// entity object properties\n\t\tentityObj._className : String\n\t\tentityObj._isDirty : Boolean\n\t\tentityObj._isDeleted : Boolean\n\t\tentityObj._isTransient : Boolean\n\t\tentityObj._isStub : Boolean\n\nFor backward compatibility the `dataManager` functions `isDirty`, `isDeleted`, `isTransient` and `isStub` still remain as decprecated functions (see documentation in [release 1.1.0#tracking-and-changing-entity-object-states](https://github.com/msg-systems/datamodeljs/tree/1.1.0#tracking-and-changing-entity-object-states)). \n\n## Finding entity objects\n\nFinding entities can be reached over several ways\n\n1. `findAll` - find all objects of a given entity class\n2. `findAllDirty` - find all objects of a given entity class that are marked as `dirty`  \n3. `findAllDeleted` - find all objects of a given entity class that are marked as `deleted`  \n4. `findAllTransient` - find all objects of a given entity class that are marked as `transient`  \n5. `findAllStub` - find all objects of a given entity class that are marked as `stub`  \n5. `findById` - find all objects of a given entity class that match the given primary key  \n5. `findByExample` - find all objects of a given entity class that match a given example object  \n\nThe following finder functions only need the class name (`cls`) and will return an array of entity objects that match the finder.\n\n    \tdataManager.findAll(cls:String) : Array(entity)\n    \tdataManager.findAllDirty(cls:String) : Array(entity)\n    \tdataManager.findAllDeleted(cls:String) : Array(entity)\n    \tdataManager.findAllTransient(cls:String) : Array(entity) \n    \tdataManager.findAllStub(cls:String) : Array(entity)\n\nExample:\n```js\n    dm.findAll(\"Verweis\")\n    dm.findAllDirty(\"Knoten\")  \n    dm.findAllDeleted(\"Knoten\")\n    dm.findAllTransient(\"Knoten\") \n    dm.findAllStub(\"Knoten\")\n```\n\n----------\n\nFinding entities by primary key is quite easy by providing the desired primary as additional parameter (`id`). The `id` is defined as `Any` since it depends on the class specification. Basically it is either a `string` or a `number`.\n\n    \tdataManager.findById(cls:String, id:Any) : entity\n\nExample:\n```js\n    dm.findById(\"Verweis\", 1)\n```\n\n----------\n\nFinding entities by a given `example` object is available via the `findByExample` function. \n\n    \tdataManager.findByExample(cls:String, example:Object) : Array(entity)\n\nExample:\n```js\n    // find all Verweis entities where verweisTyp is equal to \"Symbol\"\n\tdm.findByExample(\"Verweis\", { verweisTyp: \"Symbol\" })\n    // find all Verweis entities where verweisTyp is equal to \"Symbol\" and knoten is equal to Knoten with id 1\n\tdm.findByExample(\"Verweis\", { verweisTyp: \"Symbol\", knoten: 1 })\n```\n\n## Partial entity import\n\nSometimes it is useful or necessary to import data into existing entity objects. The problem with this is that any existing relationship with other others or from other objects should not be influenced. Importing data fields into an existing entity is possible with the `import` function. A given example object (`example`) will hereby be merged into an existing entity object (`obj`). \n\n    \tdataManager.import(cls:String, obj:Object, example:Object) : entity\n\nExample:\n```js\n    dm.import(\"Verweis\", dm.findById(\"Verweis\", 1), { verweisTyp: \"Copy\", anzeigename: \"Partial change\" })\n```\n\nThis is often needed, if a JavaScript application edits entities then stores changes to any other system and gets a newly changed object as result back. In this case the old 'saved' object still exists in the `dataManager` and it must be updated with the new result of the service. \n\n## Debugging entity class and objects\n\nSince the `dataManager` does quite some jobs for us and a data model might be slightly huge and complex *datamodeljs* enables a little debugging help for developers. \n\n\t\tdataManager.dump() : void\n\nUsing the `dump` function on the `dataManager` shows you two lists with information:\n\n1. a full list of all defined classes and their specification\n2. a full list of all defined entity objects ordered by class\n\t- once as an arrayed list\n\t- and once as an object map allowing access by primary key\n\n \nSo at any time during development, application test and application runtime it should be possible to get an insight to what the `dataManager` handles. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsg-systems%2Fdatamodeljs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmsg-systems%2Fdatamodeljs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsg-systems%2Fdatamodeljs/lists"}