Introduction
By using the model-based framework mODELcLASSjs in an AngularJS Web Application we can implement the application more safer and easier. This article will introduce some basic usages of mODELcLASS working together with AngularJS. For checking every detail you can download the example application (Please use Firefox to open the index.html file or put the application source code to a localhost).
There is also an article that introduced some features of mODELcLASSjs written by Gerd Wagner.
Background
I'm developing a web application with a topic of public library using angularjs and also trying to make it works together with mODELcLASSjs. This article is based on my project.
Using AngularJS we can build a web application very fast, but we can also implement the application with help of mODELcLASSjs framework and get many benefits as following:
- The class/model structure will be clear;
- Constraints can be predefined during implementing a class/model;
- Based on the predefined contraints mODELcLASS can also validate the input-value;
- mODELcLASS can help us to manipulate the data with Parse cloud storage;
- Subtyping is also easier to realize.
A short introduction about mODELcLASSjs
mODELcLASSjs can be used for the model layer of a MVC JavaScript web app, which may be built, for instance, with the help of a front-end framework such as AngularJS or KnockoutJS. It provides a meta-class for creating JavaScript model classes (like Book
, Author
and Publisher
) with declarative constraint validation, storage management, and the option of multiple inheritance and object pools.
Set up a model and Define some constraints
Initializing a Book
model using mODELcLASSjs is just like draw an UML class diagram with necessary constraints but in JSON-format:
<code>Book = new mODELcLASS({
typeName: "Book",
storageAdapter: {
name: "Parse-REST-API",
predefinedAttributes: ["objectId","createdAt","updatedAt"],
applicationId: "XYZ",
restApiKey: "XYZ"
},
properties: {
"isbn": {range:"NonEmptyString", isStandardId: true, label:"ISBN", pattern:/\b\d{9}(\d|X)\b/,
patternMessage:'The ISBN must be a 10-digit string or a 9-digit string followed by "X"!'},
"title": {range:"NonEmptyString", min: 2, max: 50, label:"Title"},
"year": {range:"Integer", min: 1459, max: util.nextYear(), label:"Year"},
"edition": {range:"PositiveInteger", optional: true, label:"Edition"}
}
});
</code>
typeName
: defines the name of this class type (it's recommended to set the same name as the class variable). storageAdapter
: mODELcLASSjs provides several adapters like "LocalStorage", "Parse-REST-API" and "XHR/MySQL" for different process. We will use Parse cloud storage, which contains some predefined attributes, and need "Application ID" and "REST API KEY" to get access to the server. properties
: all the properties of Book
will be defined here and each property has following constraints:
range
: it's necessary to contain range
in each property, which can be declared as String, NonEmptyString, Integer, NonNegativeInteger, PositiveInteger, Decimal, Date and Boolean; - one of these properties must be the standard identifier by defining
isStandardId: true
; label
is the name of the property; pattern
describes a regular expression, that the value of this property should be matched; patternMessage
can be used for specifying an error message according to the pattern
that you've defined. min/max
defines the interval of numeric or string length; optional
can be true if the value of this property is optional (false by default); unique
can be true if the value of this property is unique; minCard/maxCard
can be defined when a multi-valued property is array-valued or map-valued. minimum/maximum cardinality of a multi-valued property (default: 1/1), maxCard may be infinity.
Working together with AngularJS we put the code into the "Run Blocks", so that the Book
will be setup once the application runs.
Manipulate data with Parse.com
We've created a model Book
as an instance of mODELcLASS, its storageAdapter
has been also defined at the same time.
Suppose we've collected the user inputs about a book into $scope.book
, with the help of mODELcLASSjs we can easily implement the basic CRUD operations:
Operation | Request Method | Angular Click Event | mODELcLASS Function |
Creat | POST | $scope.addBook() | Book.add( $scope.book) |
Read | GET | $scope.loadAll() | Book.loadAll( { continueProcessing: function (e, obj) {...}}) |
Update | PUT | $scope.updateBook() | Book.update( $scope.book) |
Delete | DELETE | $scope.destroyBook() | Book.destroy( $scope.book) |
By loading all the book instances from Parse.com we are using Book.loadAll(...)
with a continueProcessing(e, obj)
function, which is used for asynchronous communications:
e
is designed for getting an error message, but here its value will automatically be null
. obj
is response object, which contains all book objects. For instance there are two book records {isbn: "0000000000", title: "book1", ...} and {isbn: "1111111111", title: "book2", ...} in database, then:
- the form of
obj
is Object { 0000000000: Object, 1111111111: Object}
; - each Object within the braces is the corresponded book instance;
- we can also converse the response
obj
to array using Object.keys( obj).map( function ( k) { return obj[k];})
.
So for loading all book instances and combining these data with Angular front end, we assign all records to $rootScope.books
:
<code>Book.loadAll( { continueProcessing: function ( e, obj) {
$rootScope.books = Object.keys( obj).map( function ( k) { return obj[k];});
}});
</code>
Validation
We had also defined constraints for each property of class Book
. If we want to validate the user inputs and get the validation's message, it's so easy, that we only need to write one code line for each property. As an example, to validate the property isbn
:
<code>Book.check( "isbn", $scope.book.isbn).message;
</code>
Subtyping
Using mODELcLASSjs to implement Subtyping we can add supertype
during initializing a subclass.
Suppose we define a mODELcLASS Person
, and want to define a subtype Student
(without constraints):
<code>
Person = new mODELcLASS({
typeName: "Person",
...
properties: {
"personId": {range:"Integer", isStandardId:true, label:"Person ID"},
"name": {range:"String", label:"Name"}
}
});
Student = new mODELcLASS({
typeName: "Student",
...
supertype: Person,
properties: {
"studNo": {range:"Integer", unique:true, label:"Student no."}
}
});
</code>
The Person's properties will naturely be inherited by Student.
One Issue
mODELcLASSjs is easy to use, but please be attention that mODELcLASSjs will only check the value of a property, of which has been assigned, otherwise mODELcLASSjs will not make validating in the background, even the property had been defined as a Standard ID. Because of this problem, we'd better predefining an empty instance of book model into $scope
at the very beginning of BooksController for instance:
<code>$scope.book = {
isbn: "",
title: "",
year: 0
};</code>