# NanoBind - Templating and Data binding on DOM in JS

Sorry my poor English, my native language is Pascal.

## Samples
here: http://helium.asmer.org.ua:10011

## License
MIT

## Idea

### Declarative better than imperative

Instead of write loops in code and/or template, just give a structure and get a view.

### Clean HTML

Good HTML declarative enough, to avoid using some template engine markers like `{{ varname }}`, `{% control structure syntax %}` and so on. Also, usually template loops
requires some loopable data structure, passed into temlate engine to render.

### Less better, than more

It's not jQuery, Angular or Meteor. It's just a way to put and get data from DOM Tree more transparent and declarative. And **NanoBind** does nothing to conflict with
any other framework.

## Requirements

Support of ES6 `Proxy` object.

## It's a magic

Basic initialization:
```html
<p id='p1'></p>
<p id='p2'></p>
<input type='text' value="some input value" name='sampleText'>
```
```javascript
var $s = nbInit({p1: "first paragraph"});

$s.p2 = "second paragraph";

alert($s.sampleText);
```

NanoBind gives ability to read and write data into DOM tree, using usual JS data structures: strings, booleans, arrays and objects. Access to DOM tree works 
on `ids`, `names`, `classes` and `CSS-Selectors`

## DOM Tree search priorities

When you accessing to some property in bound object (`$s` in this document), for example `$s.input` NanoBind tries to find DOM elements in next priority order:
- Element with `id="input"`, if not found, it tries to find,
- Element(s) by CSS-Selector "input", e. g. some tag(s) `input`, if not found, it tries to find,
- Element(s) with `name="input"`. You can use it for radiobuttons, or, if not found,
- Element(s) with one of classes, equals `input`. 

## Anisotropic dataflow

**Nanobind** offers *anisotropic* behavior for data passed and read in DOM tree. I. e. when you set something to DOM and read this property, it's sometimes aren't
same value.

```html
<select id="select">
    <option value="">--</option>
</select>
```

```javascript
//filling dropdown list
$s.select = {"": "--",
             M:  "Male",
             F:  "Female",
             X:  "Xenomorph"};
//reading dropdown value:
$s.select; //evaluates as "", because it's first value in list
//set it to Xenomorph:
$s.select = 'X'; 
```

In sample above we are populate select with options from associative array, than read value of select, and it's *not associative array*, it's *value of select*. Than we
set other value for this select using `option` `value`.

## Setting and templating

How to set some HTML values and properties, using **NanoBind**, and how data types and structures interpreted.

### Strings and Numbers

Are used for setting **DOM** `value` or `innerText`:

```html
<span></span>
<input type='number' placeholder="editable number"/>
```
```javascript
$s.span = "Some text in span";
$s.input = "45";
```

Please note, than both tags will be found by **CSS-Selector**. **NanoBind** will change *all* matched tags found.

#### `input[type='radio']`

Radiogroups works as expected:

```html
<label>
    <input type="radio" class="radio" name="sex" value="M">
    <span class="description">Male</span><br>
</label>
<label>
    <input type="radio" class="radio" name="sex" value="F">
    <span class="description">Female</span><br>
</label>
```

```javascript
$s.sex = "M"; //set to male sex, or
//$s.sex = "F"; //set to female sex


$s.sex; //evaluated as 'M' or 'F', depending on current radio group value.
```

### Boolean

Booleans has two roles in **NanoBind**

#### `input[type='checkbox']`

On checkboxes, booleans, naturally, turns check on or off:

```html
    <input type='checkbox' id='check1'>
```

```javascript
$s.check1 = true;
$s.check1 = false;
```

#### On other elements

it turns visibility on or off:

```html
<div class="alert alert-danger">
  <strong>Error</strong>Some error happens
</div>
```

```javascript
$s["alert-danger"] = !!errorMessage; //if errorMessage is empty, turn off that div.
$s["alert-danger"] =   errorMessage; //and set innerText to errorMessage value.
```

### Arrays 

Usually arrays means some repeating one type data values, should be represented as list or table in `HTML`:

#### `round robin`

```html
<div id='menu'>
    <a href='#' style='color: red;'></a>
    <a href='#' style='color: green;'></a>
</div>
```

```javascript
$s.menu = ["Main", "About Us", "Other links", "second green link"];
```

In sample above **NanoBind** create four elements `a`, using round robin, and fills them with data from array. 

#### Arrays and many found elements

If **NanoBind** found more than one matched element with **no children** or count of elements equals array length, it set them one-to-one, w/o multiplication:

```html
<label><input type='checkbox' class='check'><span class='description'></span><br/></label>
<label><input type='checkbox' class='check'><span class='description'></span><br/></label>
<label><input type='checkbox' class='check'><span class='description'></span><br/></label>
```

```javascript
$s.check = [false,false,false]; //turn all checkboxes with class `check` to unchecked state
```

In other cases every matched element will be set with array (with multiplication of subnodes)

#### Arrays and recursion.

**NanoBind** can fill tables with array of arrays recursively:

```html
<table>
    <tbody id="numberTable">
        <tr>
            <td><input>table template line 1</td>
            <td style='background-color: blue;'>table template line 1</td>
        </tr>
        <tr style='background-color: gray;'>
            <td>table template line 2</td>
            <td style='background-color: red;'>table template line 2</td>
        </tr>
    </tbody>
</table>
```

```javascript
$s.numberTable = [[[1],2,[3,"input"],4,5,6],
                    [7,8,9,10,11,12],
                    [13,14,15,16,17,18],
                    [19,20,21,22,23,24]],
```

Due to `input` in some `td`, some values passed as one-element array to get one level deeper into `input`. As you can see, `round-robin` works recursively on `tr` and 
`td` levels. Also, you may set more than one element in deepest arrays like `[1]` and `[3]` and got third level of recurrent multiplication: many `input`s in `td`.


### Associative Arrays (Objects)

`Objects` used for set some paired values.

#### `select > option`, `ul|ol > li` and other simple cases.

```html
<select id="select">
    <option value="">--</option>
</select>
```

```javascript
//filling dropdown list
$s.select = {"": "--",
             M:  "Male",
             F:  "Female",
             X:  "Xenomorph"};
```

works as expected - multiplying option with object, using keys as `value` and values as `innerText`.

#### Setting element properties

Sometimes you need to set some set of DOM HTML Element properties, and you can use objects for this:

```html
<input id='someInput' />
```

```javascript
$s.someInput = {type: "number", placeholder: "percents", max: "100", min: "0", value: 50, onchange: function(){
    alert(this.value);
}}
```

Event handlers can be passed as HTML Element properties too.

#### Using key as class

To be free from HTML structure and key order in object, you can use object keys as class name:

```html
<table>
    <thead>
        <th> age</th>
        <th> married</th>
        <th> surname</th>
        <th> name</th>
        <th> note</th>
    </thead>
    <tbody id="hashTable">
        <tr>
            <td class='age'></td>
            <td><input type='checkbox' class='married'></td>
            <td class='surname'></td>
            <td class='name'></td>
            <td><textarea class='note'></textarea></td>
            <td> just some another field, not bound to data</td>
        </tr>
    </tbody>
</table>
```

```javascript
$s.hashTable = [{ name: "Ivan",
                  surname: "Ivanovv",
                  age: "57",
                  note: {value: "Buhaet", name: 'ivanovvsTextArea'},
                  married: true},
                { name: "Petr",
                  surname: "Petroff",
                  age: "17",
                  note: "Tyolki v golove",
                  married: false,
                  },
                { name: "Mary",
                  surname: "Tester",
                  married: true,
                  note: "Ovulyashka",
                  age: "27"} ];
```


## Getting data

**NanoBind** *attaches your data to DOM HTML Element* when you set it as `nbData` property, and use it as "template" for read. But, in some cases it doesn't returns updated
data, but other value.

### Numbers

Can't be returned at all, because DOM doesn't use numbers even for number properties like `value` in `li` or `value` in `input[type='number']`.

### Strings

Are usual result of any read for primitive values.

### Boolean

Returned only when `input[type='checkbox']` reading. **You can't read visibility status as `boolean`**.

### Reading inputs

...like `textarea`, `select`, `input` works as expected - returns what user has selected or entered into inputs.

### Complex data structures like arrays and objects
...reading recursively, as passed when set. But some collisions possible with nested inputs.



## Tricks

### Appendable Edit

simple sample of editable data set with minimal event handling

```html
<table>
    <thead>
        <th> age</th>
        <th> married</th>
        <th> surname</th>
        <th> name</th>
        <th> note</th>
    </thead>
    <tbody id="hashTable">
        <tr>
            <td class='age'></td>
            <td><input type='checkbox' class='married'></td>
            <td class='surname'></td>
            <td class='name'></td>
            <td><textarea class='note'></textarea></td>
            <td><button class='btnDel'>x</button></td> 
        </tr>
    </tbody>
</table>
```

```javascript
var persons = [{ name: "Ivan",
                  surname: "Ivanovv",
                  age: "57",
                  note: {value: "Buhaet", name: 'ivanovvsTextArea'},
                  married: true},
                { name: "Petr",
                  surname: "Petroff",
                  age: "17",
                  note: "Tyolki v golove",
                  married: false,
                  },
                { name: "Mary",
                  surname: "Tester",
                  married: true,
                  note: "Ovulyashka",
                  age: "27"} ];

$s.hashTable = persons;

function btnDel(){
    var thisLine = this.parentElement.parentElement;
    thisLine.remove();
    $s["#hashTable tr:first-child button"] = false; //turn off first delete button 
};

$s.btnDel = {onclick: btnDel};

$s.addPerson = {onclick: function(){
    var newLine = document.getElementById('hashTable').copy.children[0].cloneNode(true);
    newLine.nbData = Object.assign({},persons[0]);
    document.getElementById('hashTable').appendChild(newLine);
    $s.btnDel = {onclick: btnDel};
}}
```

Overall, I'm not sure than event handlers and other imperative things might be used this way with **NanoBind**. Maybe frameworks better for this.

## TODO

### Recursive HTML
For building nested structures from same HTML subtree like tree control or menu

### Substructure `Proxy`
Now data passed only when you're rewrite some key in `$s`. Any changes like `$s.hashTable[0].age = 19` has no effect, because object instance are same as before.

### nbData collisions.
Now every element has one or zero nbData, but any element matches on *many* selectors, potentially with many `nbData`. So, nbData should be converted to associative array
'selector': data.