Skip to content

Latest commit

 

History

History

seemple-array

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Seemple.Array class example

This example explains a purpose of Seemple.Array. Before reading you need to be familiar with Seemple.Object. A short tutorial about the usage of Seemple.Object lives there.

Let’s say the task is to display a list of some people as a table. So as not to make the example more complicated, let’s place the prepared data into data variable.

// randomly generated names and phones
const data = [{
        name: 'Ida T. Heath',
        email: '[email protected]',
        phone: '507-879-9766'
    }, {
        name: 'Robert C. Burkhardt',
        email: '[email protected]',
        phone: '321-252-5698'
    }, {
        name: 'Gerald S. Reaves',
        email: '[email protected]',
        phone: '765-431-5347'
}];

At the beginning, as usual, let’s create HTML layout.

<table class="users">
  <thead>
    <th>Name</th>
    <th>Email</th>
    <th>Phone</th>
  </thead>
  <tbody><!-- the list of users will be here --></tbody>
</table>

Declare Users collection which is inherited from Seemple.Array.

class Users extends Seemple.Array {

}

Define "itemRenderer" property which is responsible for the way the elements of the array will be rendered on the page.

get itemRenderer() {
    return '#user_template';
}

In this case, the selector, referring to a template in HTML code, has been given as a value.

<script type="text/html" id="user_template">
 <tr>
   <td class="name"></td>
   <td class="email"></td>
   <td class="phone"></td>
  </tr>
</script>

itemRenderer property can get other values, including function or HTML string.

And set the value of Model property, defining the class of elements which are contained in the collection.

get Model() {
    return User;
}

We will create User class a bit later, let’s define the constructor of the newly-created collection class first.

constructor(data) {
    super();
    this
        .bindNode("sandbox", ".users")
        .bindNode("container", ":sandbox tbody")
        .recreate(data);
}

While creating the instance class:

  • sandbox property is bound to .users element creating a sandbox (class boundary effect on HTML).
  • container property is bound to :sandbox tbody element determining HTML node where the rendered array items will be inserted into.
  • Add the passed data to the array with the help of recreate method.

That's good enough. But we're going to use all the awesomeness of ECMAScript 2015 and we're going to use super call to fill the collection with passed data.

constructor(data) {
    super(...data)
        .bindNode('sandbox', '.users')
        .bindNode('container', ':sandbox tbody')
        .rerender();
}
  • Add new items to the collecton via super call (which does the same as Seemple.Array.apply(this, data) would do).
  • sandbox property is bound to .users element creating a sandbox.
  • container property is bound to :sandbox tbody element.
  • Call rerender method to render the collection (since we've bound container later than added new items).

Now declare a "model". User class is inherited from the familiar Seemple.Object.

class User extends Seemple.Object {
    constructor(data) { ... }
}

Set in the data passed to the constructor with the help of setData method or, as we always do, set the data via super call.

super(data);

Next, wait for "render" event which only fires out when the corresponding HTML element has been created but it hasn’t been inserted into the page yet. Bind the relevant properties to the corresponding HTML elements in the handler. When the property value is changed, innerHTML of the set element will be changed as well.

this.on('render', () => {
    this.bindNode({
        name: ':sandbox .name',
        email: ':sandbox .email',
        phone: ':sandbox .phone'
    }, Seemple.binders.html());
});

There is also an option to listen for "render" event via creating a special virtual method for a model called onRender (check out the doc) but for demonstrational purposes let's simply use on method call.

In the end, create the instance of Users class, having passed the data as an argument.

const users = new Users(data);

That’s it. On page reloading you will see a table with the list of users.

Demo

Now open the dev console and type:

users.push({
  name: "Gene L. Bailey",
  email: "[email protected]",
  phone: "562–657–0985"
});

As you see, a new element has been added to the table. And now call:

users.reverse();

or any other array method (sort, splice, pop...). Besides its own methods, Seemple.Array contains all the methods of a JavaScript array without any exception. Then,

users[0].name = "Vasily Pupkin";
users[1].email = "[email protected]";

As you see, you don’t have to watch changes in the collection manually, the framework catches data changes and alters DOM by itself. It’s incredibly convenient.

Remember, Seemple.Array supports its own set of events. You can catch any change in the collection: adding, deleting, re-sorting of elements with the help of on method.

users.on("addone", evt => {
    console.log(evt.addedItem.name);
});
users.push({
    name: "Clint A. Barnes"
});

(it will print the name of the added user in the console)


As it's said at the documentation for itemRenderer you're able to define the item renderer on Model class level. This is the answer on a frequently asked question "why should I declare a renderer on the collection level". Instead of using itemRenderer you can define renderer property for the Model class.

class User extends Seemple.Object {
    get renderer() {
        return '#user_template';
    }
    constructor(data) { ... }
}

There are more ways to make such application: you don't actually have to define a model class if it does not contain any serious logic. There is an example of the same application but using only single class:

class Users extends Seemple.Array {
    get itemRenderer() {
        return '#user_template';
    }
    constructor(data) {
        super(...data)
            .bindNode('sandbox', '.users')
            .bindNode('container', ':sandbox tbody')
            .rerender();
    }
    onItemRender(item) {
        // item is a simple object so we're going to use
        // a static version of bindNode
        Seemple.bindNode(item, {
            name: ':sandbox .name',
            email: ':sandbox .email',
            phone: ':sandbox .phone'
        }, Seemple.binders.html());
    }
}

Also you can use a bindings parser which is turned on by default at Seemple.Array class and declare the item renderer inside the class and don't define any templates at HTML code.

class Users extends Seemple.Array {
    get itemRenderer() {
        return `
        <tr>
          <td class="name">{{name}}</td>
          <td class="email">{{email}}</td>
          <td class="phone">{{phone}}</td>
        </tr>`;
    }
    constructor(data) {
        super(...data)
            .bindNode('sandbox', '.users')
            .bindNode('container', ':sandbox tbody')
    }
}