Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

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 expressions

Also 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)