Today I’ll show a little library (~6kb) written with ES6 + Babel. It’s called Gisele, like the super model.
Gisele is tiny wrapper around JS plain objects combined with a little type validation system.
Starting with a simple use case, we are going to write a wrapper for a
User entity, which has four fields:
active. Both name and email are strings, id is a read-only number and active is a boolean.
This is what a User model looks like:
From now on, we can make instances of our User structure with actual user data:
Bob is now an instance of User, an object with nothing unusual. But there’s a trick I used from ES5 that allows to define getters and setters for any object property.
That’s where the wrapper comes in: we can change the value of our model attributes, but they are actually written to an internal state object. Only when we call a method that this changes are actually applied over the initial data.
The fields you give to
Model.create() are transformed into a custom property on each model instance. When you instantiate a model, a getter/setter pair of functions is defined for each field to handle changes. Whenever a field is changed, the setter function is called to save this in a special property. This is done with
The field values are stored apart from the instance, so the model state can be changed or restored through instance methods. This enables an easier tracking of changes to be saved. For example, a model instance can be attached to fields in a form. This works perfectly with frameworks like AngularJS. The model exposes a property
$$dirty that flags a changed model.
Look at this example:
So, in our example, if I change Bob’s name or email, this can be reverted with a simple method call. The changes can also be applied to our original data, so it becomes one (the original data + the changes).
You can see it in action here.
Along with the Model class, there’s also
Gisele.Field, which is a base class to define new fields.
Here’s an example of how I define a new field type called
Point. This snippet is ES6.
From now on, we can declare a model using this type. There is one rule to follow here: the constructor must have only one argument. Since we are dealing with model fields, which represent a single value on a model, we can’t declare a constructor that deals with more than one thing.
We can also define some methods to our new model and use arrays of values in a field:
As you can see, the field declaration is a combination of a Constructor function and a Field subclass, and the field has a mechanism to parse/serialize values. This also works as a validator to each field.
The base class to all the models has a property that points to a prototype shared with every instance of our models. I’m talking about
Model.fn, an object with some methods already defined on it.
When a new model instance is created, it receives a property named
$$, which gives access to model’s inner methods. They are not part of our model’s prototype to avoid conflicts with property names.
Let’s override the default
commit method with a new function:
Now everytime the model changes are applied, the changes are printed to the console. More methods can be added to this property as well. From a model custom method, the
commit here can be accessed as
this.$$.commit, as you can see in the Rectangle example above.
The library is available on GitHub:
Here’s the examples above running: