sentinel.js

1.2.9 • Public • Published

##Sentinel.js - A Simple template and data binding Javascript library.

##Introduction

Sentinel.js can take a JavaScript object or array and use it to expand a fragment of html using the directives specified in the html tags. It also binds the structure of the DOM and the value of its input elements (INPUT, TEXTAREA, SELECT) nodes to the object, so that when the attributes of the bound object changes, those are reflected html (Not automatically for the timebeing). In the other direction, the value on the object's bound properties will be updated on the change event of input type elements.

It uses jQuery for DOM manipulation.

##Examples

  1. Simple binding
  2. Calculations in templates
  3. Iterate over array using sn-repeat directive
  4. Accessing loop variables
  5. Using sn-show directive to hide/show elements
  6. Using sn-computed-attribute directive enable/disable attributes
  7. Capturing nodes for inserting later
  8. Building Recursive structures using the above feature
  9. Attaching events

###The simplest one

<script src="jquery.js"></script>
<script src="sentinel.js"></script>
<script>
$(function() {
    user = {"name":"Chris"};
    Sentinel.bind(user, 'user-details')
    Sentinel.syncHtml();
});
</script>
<body  sn-enabled="true">
    <div>{|user-details.name|}</div>
</body>

####Output will be something like ... ...

Chris

####Explanation

The sn-enabled="true" attribute is used to mark html nodes(with its descendants) that Sentinel should handle.

This line,

    Sentinel.bind(user, 'user-details')

binds the object in variable user to a name user-details. We can refer to the object using this name in our templates, as you can see here,

    <div>{|user-details.name|}</div>

You may have noticed that the value of the variables are wrapped in span elements when they are expanded. This is so that Sentinel can update the value when the value of the bound property changes. To see this, change the value of user.name to something else and call Sentinel.syncHtml() from firebug console. You will see the value getting updated in the html. Only the value attribute of the element will be used for binding and this updating is only triggered on the change event of the bound element.

###Calculation in templates

In your templates, you can write small segments of JavaScript by wrapping them in tildes(~). See an example below where we calculate the percentage and display if the student has passed based on the marks.

<script>
$(function() {
    user = {"name":"chris", "marks":40};
    Sentinel.bind(user, 'user-details');
    Sentinel.randomPrefix = '100';
    Sentinel.syncHtml();
});
</script>
<body sn-enabled="true">
    <div>Name: {|user-details.name|}</div>
    <div>Marks: {|user-details.marks|}</div>
    <div>Percentage: ~({|user-details.marks|}/50)*100~</div>
    <div>Passed: ~if( {|user-details.marks|} >35) "Yes";else "No"~</div>
</body>

**NOTE: You will not be able to assign values to the bound properties using this feature, because each variable reference is replaced with a function call before it is evaluated. **

###Declare a scope for an html element.

In the previous example, we used the user-details bound variable reference to access the individual properties of an object. But we can achieve the same effect by declaring a scope for a parent element. When we do that, the properties of that object can be accessed using syntax this.property. See the example below

<script>
$(function() {
        user = {"name":"chris", "marks":40};
        Sentinel.bind(user, 'user-details');
        Sentinel.syncHtml();
    });
</script>
<body sn-enabled="true" sn-scope="user-details">
    <div>Name: {|this.name|}</div>
    <div>Marks: {|this.marks|}</div>
    <div>Percentage: ~({|this.marks|}/50)*100~</div>
    <div>Passed: ~if( {|this.marks|} >35) "Yes";else "No"~</div>
</body>

The scoped variable will be available immediately in the tag that declares the scope, so that you can use it even in it's own attributes. When you use nested scopes, you can access the parent scope using parent variable. If you want to access the parent's parent, you can do it using parent-parent variable and so on to any number of nesting levels.

See the example below,

$(function() {
        kid = {"name":"Juan", "age": 25};
        parent  = {"name":"James", "age":47, "child": kid};
        grandparent = {"name":"John", "age":70, "child":parent};
        Sentinel.bind(grandparent, 'user-details')
        Sentinel.syncHtml();
    });
</script>
<body sn-enabled="true" sn-scope="user-details">
    <div>
        Name: {|this.name|}
    </div>
    <div>
        Senior: ~if( {|this.age|} >60) "Yes";else "No"~
    </div>
    <div style="padding-left:10px" sn-scope="this.child"> <!-- scope switched to user-details.child -->
        <div>
            Name: {|this.name|}
        </div>
        <div>
            Senior: ~if( {|this.age|} >60) "Yes";else "No"~
        </div>
        <div>
            Father: {|parent.name|} <!-- accessing outer scope one level up -->
        </div>
        <div style="padding-left:10px" sn-scope="this.child">  <!-- scope switched to user-details.child.child -->
            <div>
                Name: {|this.name|}
            </div>
            <div>
                Senior: ~if( {|this.age|} >60) "Yes";else "No"~
            </div>
            <div>
                Grand Father: {|parent-parent.name|} <!-- accessing outer scope two levels up -->
            </div>
        </div>
    </div>
</body>

###Iterate over an array using sn-repeat directive

When you have a sn-repeat attribute for an element as something like sn-repeat="bound-variable-reference", that element will be duplicated and added to the DOM for every item in the array pointed by the specified reference. For each iteration, the repeated item node will be evaluated in the context of the current element in the iteration, before adding it to the DOM.

See a simple example below.

<script>
$(function() {
        colors = ["red","blue","green","violet","yellow","purple"];
        Sentinel.bind(colors, 'colors')
        Sentinel.syncHtml();
    });
</script>
<body sn-enabled="true">
<div sn-repeat="colors">{|this|}</div>
</body>

####Output will be something similar to

...
...
<body sn-enabled="true">
    <div>red</div>
    <div>blue</div>
    <div>green</div>
    <div>violet</div>
    <div>yellow</div>
    <div>purple</div>
</body>

####Accessing the loop index

You can access the index of the current loop by using the special reference {|loop-index-0|}. For accessing index of outer loops, you can use {|loop-index-1|}, {|loop-index-2|} and so on. See an example below,

<script>
$(function() {
        colors = ["red","blue","green","violet","yellow","purple"];
        Sentinel.bind(colors, 'colors')
        Sentinel.randomPrefix = 100;
        Sentinel.syncHtml();
    });
</script>
<body sn-enabled="true">
<div sn-repeat="colors">
    <div sn-repeat="colors">
        <div sn-repeat="colors">
            {|loop-index-0|}, {|loop-index-1|}, {|loop-index-2|}
        </div>
    </div>
</div>
</body>

Using sn-show directive to hide/show elements

This directive will evaluate the specified expression as javascript and hides the element if the expression evaluates to false. For eg,

<li sn-show="{|this.mark|} > 0">

Using the sn-computed- prefix to make an attribute evaluated

When an attribute have the prefix sn-computed-, the given expression will be evaluated as javascript, and if the value is not false, then that value will be assigned to an attribute whose name is derived by removing the sn-computed- prefix in the original attribute.

<option sn-computed-selected="if ({|this|} == {|parent.color|}) 'selected';else false;">{|this|}</option>

Here the expression if ({|this|} == {|parent.color|}) 'selected';else false; is evaluated and if it evaluates to false, the complete attribute is removed. Else the value will be assigned to the selected attribute.

###Capturing nodes for inserting later

This used to capture a segment of html so that it can be inserted somewhere later. This is mostly useful for creating recursive structures. You can capture an html node by specifying a sn-capture-id attribute. You can insert a captured node by using the sn-insert attribute by specifying the id of the previously captured node as its value. See and example below.

<body>
<div sn-enabled="true" sn-repeat="user-details">
    <div sn-capture-id="user-details-block" style="padding-left:10px"> <!-- this node will be captured -->
        <div>{|this.name|}</div>
        <div>{|this.age|}</div>
        <div sn-repeat="this.children" sn-insert="user-details-block"></div> <!-- to insert here -->
    </div>
</div>
</body>

Please note that, for recursive structures, as shown above, you should use sn-insert along with sn-repeat or sn-show so that the nesting stops at some level.

###Attaching events

You can attach event handlers, which are part of the scope, to elements using the sn-event- prefix. If you do this

<a sn-event-click="this.clickEventHandler">Click</a>

The the handler will be bound to the event name that follows the *sn-event- prefix, ('click' in the above example). Please note that you can only bind functions that are part of the scope object. When the event handler is called, it will be called in the context of the object which it is a part of. See the following.

<script>
$(function() {
var a = {
        "name":"Jack",
        "changeName": function() {
            this.name = "Jill";
            Sentinel.syncHtml();
        }
    };
Sentinel.bind(a, 'scope');
Sentinel.syncHtml();
});
</script>
<div sn-enabled="true" sn-scope="scope">
    Name : {|this.name|} <a href="#" sn-event-click="this.changeName">Change Name</a>
</div>   

The function in changeName property of the object uses this to change properties of the object it is a part of. So unlike a normal event handler, this function will not have access to the DOM element, but only to the object that it is a property of. You can pass any number of parameters to the function using attributes of the form sn-parameter-0, sn-parameter-1 and so on.

###Disclaimer

This is an expirimental JS framework. The strenghts and weaknesses of this approach are not fully know right now. But this should work well for simple use cases. If you find that something is not working, you can take a look into the code, as I have tried to make it easily readable. If that fail's please add a bug report in the issue tracker. Please try to attach a sample code that shows the issue.

Readme

Keywords

Package Sidebar

Install

npm i sentinel.js

Weekly Downloads

1

Version

1.2.9

License

ISC

Last publish

Collaborators

  • sras