Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dsheiko/ng-template
⛔️ [DEPRECATED] Light-weight DOM-based template engine, inspired by AngularJS
https://github.com/dsheiko/ng-template
deprecated dom-based dom-manipulation obsolete template-engine typescript
Last synced: about 1 month ago
JSON representation
⛔️ [DEPRECATED] Light-weight DOM-based template engine, inspired by AngularJS
- Host: GitHub
- URL: https://github.com/dsheiko/ng-template
- Owner: dsheiko
- Created: 2016-07-28T15:08:54.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2018-05-29T11:54:16.000Z (over 6 years ago)
- Last Synced: 2024-11-14T10:37:03.678Z (about 2 months ago)
- Topics: deprecated, dom-based, dom-manipulation, obsolete, template-engine, typescript
- Language: JavaScript
- Homepage: https://dsheiko.github.io/ng-template/
- Size: 1.54 MB
- Stars: 3
- Watchers: 4
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# ngTemplate 1.0
[![NPM](https://nodei.co/npm/ng-template.png)](https://nodei.co/npm/ng-template/)
[![Build Status](https://travis-ci.org/dsheiko/ng-template.png)](https://travis-ci.org/dsheiko/ng-template)
`ngTemplate` is a light-weight DOM-based template engine, inspired by AngularJS.
`mustache.js`, `Handlebars` or `_.template` are all nice and shiny, until it comes to a form.
With every rending these template engines replace the bound DOM subtree
and the state of inputs gets lost.`ngTemplate` treats the DOM carefully. It modifies the exact target nodes gracefully
according to the directives and actual state.## Motivation
* Progressive enhancement friendly: Server-side generated HTML can be fully ready for presentation. During `ngTemplate` synchronization it will be updated according to element directives and a provided state
* HTML compliant: `data-*` - directives instead of foreign concepts such as `{{foo}}`, `[hidden]`, `*ngFor`, `[(ngModel)]`
* Concern separation: Presentation state logic decoupled from the view
* Performance: `ngTemplate` modifies DOM nodes by state diff; it touches the DOM only when it's necessary
* Easy to catch up: Familiar for Angular folks directives such as `data-ng-if`, `data-ng-switch`, `data-ng-for` and a few extra intuitive e.g. `data-ng-text`, `data-ng-class`
* Really small library: minimized gziped [size is 4K](https://raw.githubusercontent.com/dsheiko/ng-template/master/dist/ngtemplate.glob.min.js)
* Definitely Typed: IDE checks on the fly if you violate any of declared interfaces (if not the compiler does)## How does it work?
Templates are written with HTML that contains `ngTemplate`-specific data attributes (`data-ng-*`):
```html
Name
Name is required
Hero Power
Nothing here
Power is required
Submit```
`ngTemplate` synchronizes the template with the passed state. Let's say we have the template HTML within a DOM element,
so we initialize the template like `let template = new NgTemplate( el );`. Alternatively we can populate
the bounding element with the template from the passed string: `let template = new NgTemplate( el, 'Hello!' );`
As soon as we have `template` object we can sync it to a specified scope. E.g. `template.sync({ foo: true });` makes
variable `foo` available for template expressions, `template.sync({ foo: { bar: true }});` gets accessable as `foo.bar````javascript
import { NgTemplate } from "ng-template";let el = document.querySelector( "#heroForm" ),
elName = document.querySelector( "#name" ),
elPower = document.querySelector( "#power" );// Bind the template
let template = new NgTemplate( el );// Set the scope (view state)
let scope = {
powers: [ "-", "Really Smart", "Super Flexible",
"Super Hot", "Weather Changer" ],
power: {
valid: true
},
name: {
valid: true
},
form: {
valid: false
}
};
// Sync to the scope
template.sync( scope );
```## Live Demo
[ngTemplate Demo: Hero](https://dsheiko.github.io/ng-template/demo.html)
## Installing
You can get `ngTemplate` via npm.
```
npm i --save ng-template
```## Accessing module
### TypeScript
```javascript
import { NgTemplate } from "ng-template";
const template = new NgTemplate( node );
```### CommonJS
```javascript
var NgTemplate = require( "ng-template" ).NgTemplate,
template = new NgTemplate( node );
```
In order to adapt your source for browser, you can either [browserfy](http://browserify.org/) or
load your modules with [SystemJS](https://github.com/systemjs/systemjs)```
System.config({
paths: {
"*": "node_modules/*"
},
packageConfigPaths: [ "./node_modules/*/package.json" ]
});
```### VanillaJS
```HTML```
```javascript
var template = new NgTemplate( node );
```## API
```javascript
import { NgTemplate } from "ng-template";
let template = new NgTemplate( el, tpl ); // compile template
template.sync( scope ); // render on the first run and after synchronize it with the scope
// optional
console.log( template.report() ); // find the synchronization details
```where:
* el - a DOM element we bind to
* tpl - OPTIONAL: template code that will be injected into `el`
* scope - an object literal (template scope) whose members form the scope for template expressionsAlso can be instantiated via the factory:
```javascript
NgTemplate.factory( el, tpl )
.sync( scope );
```### Options
You can define though the constructor options the callbacks `willMount` and `didMount`. The first one gets invoked straight before the `ngTemplate` populates
bounding element's inner HTML from the template string. `ngTemplate` calls the second callback after that:```javascript
import { NgTemplate } from "ng-template";
let template = new NgTemplate( el, tpl, {
willMount: function(){
console.log( "Template is up to initial rendering" );
},
didMount: function(){
console.log( "Template just finished initial rendering" );
}
});
```## Template expressions
Template expressions are being evaluated in the given `scope`. So we can reference scope variables within template e.g.
`data-ng-if="foo"` refers to `foo` variable of the scope and therefore:```javascript
template.sync({ foo: true }); // show element
template.sync({ foo: false }); // hide element
```We access scope objects the same way we do it in JavaScript e.g. `data-ng-if="foo.bar"` refers to `foo.bar` and can be toggled like this:
```javascript
template.sync({ foo: { bar: true } }); // show element
```Expressions may have mixed scope variables and primitives:
```
data-ng-if="foo && bar.baz || false"
data-ng-if="foo + 10 > 20"
```We can pass rendering helpers (e.g. transformers) with the scope. For example we pass `decorator` to the directive `data-ng-if="decorator(foo)"` this way:
```
{
foo: "foo",
decorator: function( val ){
return "decorated " + val;
}
}
```Expressions are evaluated in the context of the target element, so we can access the element with `this`:
```
data-ng-if="foo && this.checked"
```> :exclamation: NOTE: In order to gain better performance keep to primitive expressions especially in cyclic directives e.g. `data-ng-text="foo.bar.baz"`,
> `data-ng-text="!foo.bar.baz"`, `data-ng-text="'string here'"`, `data-ng-if="foo.bar > baz.quiz"`, `data-ng-text="foo + 10`,
> `data-ng-if="true"`, `data-ng-prop="'disabled', true || false"`, `data-ng-data="foo || bar, baz"`.
> Such expressions are being evaluated without use of `eval()` and therefore the process takes much less time and resources.
> You can check how the parser treats your expressions by studying content of `template.report().tokens` array## ngTemplate Report
You can get template synchronization details like that:
```javascript
let tpl = new NgTemplate(
document.createElement( "div" ),
""
);tpl.sync({});
console.log( tpl.report().errors ); // [ "'foo.bar.baz' is undefined" ]```
## Directives
### ngText
We use `ngText` to modify element's `textNode`
#### Syntax
```
```
#### Examples
```javascript
(new NgTemplate( document.body , `` ))
.sync({ foo: "Foo" });console.log( document.body.innerHTML ); // Foo
```HTML gets automatically escaped:
```javascript
(new NgTemplate( document.body , `` ))
.sync({ foo: "" });console.log( document.body.innerHTML ); // <button>
```### ngProp
We use `ngProp` to modify element's properties
#### Syntax
```
// Can be applied multiple times on element
```
#### Examples
```javascript
(new NgTemplate( document.body , `` ))
.sync({ isDisabled: true });console.log( document.body.innerHTML ); //
```### ngAttr
We use `ngAttr` to modify element's attributes
#### Syntax
```
// Can be applied multiple times on element
```
#### Examples
```javascript
(new NgTemplate( document.body , `` ))
.sync({ required: true });console.log( document.body.innerHTML ); //
```### ngData
We use `ngData` to modify element's dataset
#### Syntax
```
// Can be applied multiple times on element
```
#### Examples
```javascript
` ))
(new NgTemplate( document.body , `
.sync({ value: "1960-10-03" });console.log( document.body.innerHTML ); //
```### ngClass
We use `ngClass` to modify element's `classList`
#### Syntax
```
// Can be applied multiple times on element
```
#### Examples
```javascript
(new NgTemplate( document.body , `` ))
.sync({ isHidden: true });console.log( document.body.innerHTML ); //
```
or
```javascript
(new NgTemplate( document.body , `` ))
.sync({ isHidden: true, className: "is-hidden" });console.log( document.body.innerHTML ); //
```### ngIf
We use `ngFor` to toggle visibility of an element (subtree) within the DOM
#### Syntax
```
```
#### Examples
```javascript
let template = new NgTemplate( document.body , `Hello!` );template.sync({ toggle: false });
console.log( document.body.innerHTML ); //template.sync({ toggle: true });
console.log( document.body.innerHTML ); // Hello!
```### ngFor
We use `ngFor` when we need to generate a list of elements (subtrees)
> `ngFor` treats the generated list gracefully. It tries to maintain the nodes once appended to the DOM on list data change. However of removing rows from the list during synchronization `ngFor` can do it only if a unique row `id` property provided. Otherwise it updates the list
#### Syntax
```
```
#### Examples
```javascript
(new NgTemplate( document.body , `` ))
.sync({ rows: [ "foo", "bar" ] });console.log( document.body.innerHTML ); // foobar
```### ngSwitch
We use `ngSwitch` when we need to display on element (subtree) of a set of available options.
#### Syntax
```
```
#### Examples
```javascript
(new NgTemplate( document.body ,
`` ))
FOO
BAR
.sync({ theCase: 1 });console.log( document.body.innerHTML ); // FOO
``````javascript
(new NgTemplate( document.body ,
`` ))
FOO
BAR
BAZ
.sync({ theCase: 100 });console.log( document.body.innerHTML ); // BAZ
```### ngEl
We use `ngEl` to modify element properties
> :exclamation: NOTE: Using `ngEl` is rather discouraging as it cannot be cached and every model sync will
cause the DOM modification even if the expression of `ngEl` wasn't changed#### Syntax
```
```
#### Examples
```javascript
(new NgTemplate( document.body , `` ))
.sync({ class: "is-hidden" });console.log( document.body.innerHTML ); //
``````HTML
```## Contributing
`ngTemplate` welcomes maintainers. There is plenty of work to do. No big commitment required,
if all you do is review a single Pull Request, you are a maintainer.### How to build
This source code is written in TypeScript. In order to build the app simply run `tsc` in the root directory
### How to test
There two options. You can run unit-tests in the console:
```
npm run test
```
or you can fire up `tests/index.html` in a browser[![Analytics](https://ga-beacon.appspot.com/UA-1150677-13/dsheiko/ng-template)](http://githalytics.com/dsheiko/ng-template)