📜 ⬆️ ⬇️

Work with data models in javascript

Hello, Habraludi.

Small by little from my experience and our projects was born a small library for working with models in javascript. She is called - Model.js .

I will tell you briefly about this library and by this post I am requesting feedback from those who are creating complex javascript applications that already solve this problem in some definite way without frameworks. The opinion of those who are just looking for a suitable tool for their needs is also interesting: what tool do you need and how well does Model.js suit you ?

What for?


To simplify working with the data layer without using frameworks. The current first version of the library - v0.1, weighing about 12K - is intended to help, first of all, with data validation and event management, in particular, with data changes.
')

What is special?


Sugar. Common looking nice syntactic sugar.

The model is nowhere easier to describe.
var Note = new Model('Note', function () { this.attr('id!', 'number'); this.attr('title', 'string', 'nonempty'); this.attr('text', 'string'); }); 

Then we create entities as ordinary objects.
 var note = new Note({ id: 123, title: "Hello World" }); 


Public entity properties


Attribute value getters. In our example, this is:
 note.data.id note.data.title note.data.text 

Setters In addition to changing values, the change event is also fired.
 note.data.id = note.data.title = note.data.text = note.data = {…} 

Getter note.get(attrName[, attrName, …]) return an object with the values ​​of the requested attributes.

note.get() return a copy of all the data.

note.data() is the same as note.get() .

The note.set note.set(attrName, value) and note.set({…}) do not note.set({…}) change event.

note.hasChanged tells you if the entity data has changed since it was last saved.

note.isNew says whether entity data has been saved at all at least once.

note.isPersisted says whether recent changes have been saved.

note.bind(eventName, handler) "hangs" the handler. By the way, you can hang the handler of any event not only on a separate entity, but also on all entities of a class ( Note.bind ).

note.isValid says whether the current model data is valid.

note.errors , returns data errors, if any.

note.revert() rolls back unsaved changes and “shoots” the revert event.

It's all.

You ask, where are the methods of conservation and so on? - I will answer: the use of the library implies that the developer must implement these methods independently as required by the logic of his application.

It should be noted that with preservation there is one caveat: if the data was saved successfully, you need to inform the entity about it using the private method note._persist() , which also “shoots” the persist event.

Explanation is an example of good. Let's say our application works in a browser environment and the note.save() method must save data with the help of agax.

 Note.prototype.save = function () { var note = this; return $.ajax({ type: 'PUT', url: '/notes/'+note.data.id, data: note.data(), dataType: 'json' }).done(function (json) { note._persist(); }); } note.bind('persist', function () { $('h1', 'div#note'+this.data.id).html(this.data.title); }); note.data = { id: 123, title: "abc", text: "" } if (note.hasChanged && note.isValid) { //  Django-style note.save(); } 


Data checking


When creating a model, each of its attributes is necessarily described.
 this.attr('title', 'string', 'nonempty') 
First we specify the name of the attribute, then its validators. Validators, when the time comes, in the declared order will check the value of the attribute.

In general, validators are normal functions that take values ​​and return errors when these values ​​are invalid.
 function validateMinLength(value, minLength) { if (value.length < minLength) return 'tooshort'; } 

If you have to use a validator several times - it is reasonable to register it in order to connect by name.
 Model.registerValidator('array', function (value) { if (Object.prototype.toString.call(v) === '[object Array]') return 'wrongtype'; }); Model.registerValidator('minLength', validateMinLength); 

Model.js has several basic (already registered) validators: number , string , boolean , nonnull , nonempty and in . They, just like in our example, can be connected to attributes by name.

To pass a parameter to the validator when describing an attribute, you need to write it, as in the example below, as an array:
 this.attr('title', 'string', 'nonempty', [ 'minLength', 6 ]); 

Validators can be connected in the usual old-fashioned way, without registering them.
 this.attr('title', 'string', 'nonempty', [ 'minLength', 6 ], function (title) { if (title[0] !== title[0].toUpperCase()) return 'downcase'; }); 

All this is needed so that note.isValid can say true or false , and note.errors can return an object with errors, if any.
 note.data = { id: 'abc', title: '', text: 3 }; //    note.isValid // false note.errors // { id: 'wrongtype', title: 'empty', text: 'wrongtype' } 


Developments


There are four events: initialize , change , persist and revert . As mentioned above, you can hang the handler on a specific entity ( note.bind ), but you can hang on a class ( Note.bind ), so that the handler will be common to all entities.

initialize “fired” once when an entity is created: var note = new Note({…}) . So hanging the initialize handler only makes sense to the class.

change triggered every time an attribute value changes. There are, however, setters who do not pull change (this was discussed above).

perist - when the entity receives a signal that the changes were successfully saved.

revert - when the application logic ordered to undo the unsaved changes (using the note.revert() method).

 var ALLOWED_LANGUAGES = ['en', 'ua', 'ru']; var Note = new Model('Note', function () { this.attr('id!', 'number'); this.attr('title', 'string', 'nonempty'); this.attr('lang', 'string', 'nonempty', [ 'in', ALLOWED_LANGUAGES]); this.attr('text', 'string'); }); Note.bind('initialize', function () { if (!this.data.lang) { this.set('lang', 'en'); // change   —   ! } }); note.bind('change', function (changes) { if (changes.title) $('h1', 'div#note'+this.data.id).html(changes.title); }); 


In conclusion


Now Model.js is a pure state of the art , it is an intermediate, but surely working result. If you are interested in the library, if you have a desire to try to use it, I will be happy to answer your questions. More information about how things work can be found in the githaba documentation and tests.

In the meantime, wish the baby a good journey, because in order to become an “adult” library, you need to do a lot more work.

Source: https://habr.com/ru/post/178811/


All Articles