Typrod
Typrod (acronym of "type", "property" and "data"; and abbreviated as TPD) is an AngularJS library to synthetize info handlings based on:
- input/output displays and
- value conversions.
- Here are some words that may create confusion since also are elemental terms of JavaScript, AngularJS, CSS or even SQL. Because of this, for avoid ambiguity, our own concepts will be explicitly designated (e.g. "TPD data").
- We understand that it is a complex system, so we recommend that you read all the documentation before getting started.
- We will show the functioning in the next demos with Bootstrap as testing framework, but understand that you can really use any.
Overview
Imagine AngularJS runs, you layout with Bootstrap 4 and you need to filter, list and edit a data collection. See here a improvised live demo (with one filtering <form>
and one <table>
to manage records) to be oriented about it. As we can view, usually the following occurs:
- JSON (normally from an HTTP request) is captured.
- Its values (if necessary) must to be transformed one by one. In the example, for these types:
- Datetimes: from string format to
Date
object. - Maybe (hypothetical presumption) booleans are rarely passed from an old system with numbers (
0
/1
). Then, it becomes originals (true
/false
). - And perhaps a list of options is served (another assumption) as string with IDs joined by commas. It turns into array.
- Datetimes: from string format to
- The result are displayed inserting manually:
- Inputs (among others):
- Dates: with
<input type="date">
. - Booleans: checkboxes.
- Options:
<select>
s.
- Dates: with
- Outputs (the corresponding ones):
- Dates: typically through
date
filter. - Booleans: answering ("Yes"/"No").
- Options: selected items.
- Dates: typically through
- Inputs (among others):
- To represent these data, facing the user, captions appear situated in
<label>
s and<th>
s. - Once ready, when user interacts, every edition requires the reverse conversion of values to save the change.
Having said that, notice that these steps are repeated continuously for each info. Do you want to build another table? The same again.
Well, so, using Typrod, all of these involving processes can be only-one-time done or excessively reduced. Look the altered demo and pay attention to the differences between two systems (extra utilities remains). In this occasion, the procedure is like this:
- Firstly, some simple settings are defined by a provider, transmitting:
- For each TPD type (datetimes, booleans, options... whatever!):
- A conversion function.
- An HTML template of input.
- Output's.
- To come back to JSON, another converting.
- About TPD components (in our case, table and form), indicating mainly templates.
- For each TPD type (datetimes, booleans, options... whatever!):
- At last, in the HTML coding, empty tags are printed with some attribute directives that contains:
- The captured values.
- A proper array of TPD data (group of TPD properties that indicate TPD type, label, param name, etc.).
And voilà! It is already prepared and automated. For a future TPD component (with similar TPD types), you only need to do the 2nd step, no more!
Concepts
We will take the previous examples to show samples.
TPD type
$tpdProvider
// ...
.type('options', /*...*/)
.type(['year', 'y'], /*...*/)
.type('email', /*...*/)
.type('boolean', /*...*/);
Whatever you can think. No limits. Like simply:
$tpdProvider.type('email', {
/*fromJson: undefined,
toJson: undefined,*/
input: '<input type="email" class="form-control">',
output: '<a ng-href="mailto:{{$property.value}}">{{$property.value}}</a>'
});
To speed up settings, every TPD type name can be referenced by one or more aliases. So, instead of write entirely 'string'
, 'number'
, 'boolean'
, 'email'
or e.g. custom 'anotherNewType'
, you can put abbreviated 's'
, 'n'
, 'b'
, 'e'
and 'ant'
. Regarding the above, through:
$tpdProvider.type(['email', 'e', 'em'], /*...*/);
And linked to TPD data (explained later) like e.g.:
[
// ...
{
type: 'e',
// ...
},
// ...
]
Also we can achieve it thanks to other ways to register:
Overwriting
$tpdProvider.type('boolean', function (opts) {
// ...
return opts;
});
Starting from an already added TPD type ('boolean'
is system built-in), we can define it again. Permitted to pass an object or a function (like here). And you must to indicate either its name (not alias) or the reserved '*'
value to apply globally on all:
$tpdProvider.type('*', function (opts) {
// ...
return opts;
});
Copying
Similarly to overwrite, a new TPD type can inherit definitions of another one by the copy mechanism.
$tpdProvider.type(['year', 'y'], ['number', function (opts) {
// ...
return opts;
}]);
But it has a quirk: TPD components' ETCs (more info after) will be exactly duplicated.
TPD property
[
// ...
{
type: 'n',
name: 'maxWeight',
label: 'maxWeight',
required: true
},
// ...
]
This is each of instances of a certain TPD type (and other several fits) whose a double TPD values (see below) are part of.
TPD value
<form tpd-values="..."></form>
<table tpd-values="..."></table>
Handled in conversions whose two kinds are:
JSON
{
id: 2,
name: 'Noah Baker',
gender: 'f',
birthdate: '1963-11-28',
weight: 85.12,
email: 'tur@jedigfu.eg',
isForeign: 0
}
Original values arranged in JSON.
Formatted
{
// ...
birthdate: new Date('1963-11-28'),
// ...
isForeign: false
}
Transformed (if is needed) from aforesaid, and gave back too (cyclic process).
TPD container
<div class="form-group">
<label for="..." translate="..."></label>
<input type="..." class="form-control" id="..." ng-model="...">
</div>
...
<th scope="col" translate="..."></th>
...
<td>
<input type="..." class="form-control" ng-model="..." ng-if="...">
<span ng-if="...">...</span>
</td>
...
The repeated tags, corresponding each to a TPD property, that contains an appropriate HTML content.
ETC
It may happen that, for a concrete TPD component, we find a particular TPD type which is not structured than regularly. Take a look:
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="..." ng-model="...">
<label class="form-check-label" for="..." translate="..."></label>
</div>
So, you can define this too. We know it as exceptional TPD container (ETC).
TPD data
Grouping of TPD properties. Paste the following attribute to components:
<form tpd-data="..."></form>
<table tpd-data="..."></table>
And let Typrod start to deploy all its mechanism.
TPD component
In our case: <form>...</form>
and <table>...</table>
. Determined like:
$tpdProvider.component('form', /*...*/, { /*...*/ });
And:
$tpdProvider.component('table', /*...*/);
It is possible customize any tag with any classname, attribute, parent... Here you have some varied samples:
$tpdProvider
.component('form', /*...*/)
.component('form.form-horizontal', /*...*/)
.component('table.table.table-striped', /*...*/)
.component('form[ng-submit]', /*...*/)
.component('form.form-inline[ng-submit]', /*...*/)
.component('tbody > tr', /*...*/)
.component('form + table', /*...*/);
TPD content
Full internal HTML included in TPD components. Concatenation of TPD container and extra elements.
Advantages
Why to use Typrod?:
- Code extremely simplified. On the principle of "Write less, do more".
- Clear distinction between representation (HTML structure, class names, styles, etc.) and logical (what kind of data is it, param name, labelling, requirement, etc.). And in consequence:
- Centralization. Full logics are dumped into controllers, not also partially in its views.
- Abstraction of core data, assuming that TPD properties can share homologous behaviours.
- More maintainable and reliable source code.
- Integrated reutilization of mechanisms.
- Uniformity, homogeneity and consistency.
- Possible human mistakes avoided on bad-writing, forgetting...
- Easy and fast to migrate CSS frameworks.
- Unlimited customization. Not adaptive to any particular CSS dependencies.
- Well-known system, not new or weird:
- Specifications of TPD data are similar to databases', like in SQL table creations (column name, datatype, mandatory...).
- Use of a worldwide standard data-interchange format such as JSON.
- TPD components are descripted by CSS syntax.
- Flexibility, adaptability and variety on function arguments and option values.
Install
By NPM or with CDN embeds:
NPM
npm install typrod
Then import JQuery and AngularJS (important to do this way!) and add TPD as a dependency for your app:
const $ = require('jquery');
window.jQuery = $; // To set it as alias of "angular.element"
require('angular');
angular.module('myApp', [require('typrod')]);
CDN
<script src="https://unpkg.com/jquery@3.6.1"></script>
<script src="https://unpkg.com/angular@1.8.3/angular.js"></script>
<script src="https://unpkg.com/angular-translate@2.19.0"></script>
<script src="https://unpkg.com/specificity@0.4.1"></script>
<script src="https://unpkg.com/lodash@4.17.21"></script>
<script src="https://unpkg.com/typrod/dist/typrod.js"></script>
angular.module('myApp', ['tpd']);
Usage
TPD sets by a provider and gets by a service.
Provider
It is named $tpdProvider
, whose methods (that all return it, allowing chaining) are:
type(name, opts)
Registers a TPD type.
Param | Type | Details |
---|---|---|
name |
String | Name. |
Array | Name followed by its alias(es). | |
opts |
Object | Options (see the next section). |
Function | To overwrite:
|
|
Array | Name of copied TPD type and options (obj. or fn., like above). |
Options
Key(s) | Type | Details | Default | ||
---|---|---|---|---|---|
fromJson /toJson
|
Function | TPD value conversion from/to JSON. |
|
angular.identity / caller of own toJSON method (if exists) |
|
String | Name of factory/service. | A fn. like before is extracted. | |||
Array | Fact./serv. in inline array annotation. | ||||
input |
String | HTML template. | Tagged HTML string. If multi-level or multiple tags, you have to mark the main input element by tpd-target attribute. |
Current definition of 'string' TPD type |
|
Name of fact./serv. | Fn. extraction. | ||||
Array | Fact./serv. in IAA. | ||||
Object | JQuery element. | ||||
DOM elem. | |||||
Function |
|
||||
output |
String | Any string (plain texts, tags, interpolations...). | '{{$property.value}}' |
||
Function |
|
component(selector, content[, ec])
Registers a TPD component.
Param | Type | Details |
---|---|---|
selector |
String | CSS selector. |
content |
String | TPD content. |
Function |
|
|
ec (optional) |
Object | Exceptional TPD containers. With keys as TPD type names while each value are composed by its TPD container with the same types than content (2nd param). |
Overwriting (but basic one) is also allowed.
When Typrod activates, collects the coincidences and priors the TPD component of most CSS specificity and oldest.
Service
$tpd()
proportinates a read-only object of original setters (without defaults) of registers. Regarding the TPD components, its function arguments are disposed in arrays. And the TPD type aliases appears too, grouped by names.
Directives
All of these should only be used in TPD contents and ETCs, except the first one (whose utilitation is free).
tpd-data
Put this attribute over the TPD component tag you want to locate the concerning TPD content. It contains an array where each object must to be formed as:
Key(s) | Type | Details |
---|---|---|
type (optional) |
String | Name or alias of registered TPD type. Defaults to 'string' . |
name |
String | Name of param (where TPD value is stored). |
label |
String | Translation ID of Angular Translate. |
required (optional) |
Boolean | Input mandatority. |
min /max (optional) |
Any | Minimum/maximum of TPD value. |
And other custom ones are permitted for a not-generic using.
Also it is possible to shorthand it putting an array, instead of the object, following this order: name
, label
, required
, type
and the rest (in an object). So ['isForeign', 'foreign?', false, 'b', { customKey1: 'one', customKey2: 'two' }]
equals to { type: 'b', name: 'isForeign', label: 'foreign?', required: false, customKey1: 'one', customKey2: 'two' }
.
Besides, accompanying this directive, in the same tag, tpd-values
is placed, determining the TPD JSON values (manipulated by fromJson
and toJson
). This pure attribute is optative but its exclusion has no much sense.
tpd-property
Ubicated as attribute solely on TPD containers, Typrod turns these into a repeatedly rendered element by ng-repeat
that generates $property
as local scope variable derived from each tpd-data
's item (adjusting name
from possible alias to name and saving TPD formatted value in value
property).
If a serie of adjacent elements must to be repeated (instead of just one), substitute this directive with tpd-property-start
and tpd-property-end
as ng-repeat-start
and ng-repeat-end
do.
tpd-label
Labels the attributed element.
tpd-input
/tpd-output
As tagnames, these ones are substituted by the HTML of correspondent options of TPD types.
Predefinitions
Typrod proportions some built-in registrations:
TPD types
Name | Alias(es) | Details |
---|---|---|
'string' |
's' and 'str'
|
For single-line text. |
'search' |
Text too but within <input type="search"> . |
|
'password' |
'p' and 'pw'
|
Outputs hiding chars too. |
'number' |
'n' and 'num'
|
|
'range' |
'r' |
Percentage. |
'boolean' |
'b' and 'bool'
|
|
'date' |
'd' |
|
'time' |
't' |
|
'datetime' |
'dt' |
|
'option' |
'o' and 'opt'
|
Single <select> . You only must to transfer a string of scope's array (formed by objects with pairs id -label ) to tpd-data in options key. |
'options' |
'oo' and 'opts'
|
The same but multiple. |
'color' |
'c' |
Hexadecimal color. |
'url' |
'u' |
URL. |
'email' |
'e' and 'em'
|
E-mail address. |
'tel' |
Telephone number. |
TPD components
Forms
You have namesake 'form'
. It rawly prints <div>
s with <label>
and input, and a submit button. You have to add the ng-submit
directive by yourself and data-name
attribute as namespace of labelable elements.
Description lists
<dl>
s exposes labels in <dt>
s and outputs in <dd>
s.
Tables
-
'table'
: shows a labeled head and record rows. Place attributedata-expression
as we make ontpd-values
but with an array. -
'thead, tfoot'
: labels<th>
s. -
'tbody > tr'
: outputs<td>
s.