JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://cloud.github.com/downloads/wycats/handlebars.js/handlebars-1.0.0.beta.6.js"></script>
<script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-1.0.pre.js"></script>
<script src="http://cloud.github.com/downloads/emberjs/data/ember-data-latest.js"></script>
<!DOCTYPE html>
<html>
    <title>Users</title>
    <body>
        <script type="text/x-handlebars" data-template-name="application">
  <div id="main">
    {{ outlet }}
  </div>
</script>

<script type="text/x-handlebars" data-template-name="person">

   {{view PersonApp.SearchField placeholder="search..." valueBinding="searchText"}}

<table width="250px">
<thead>

{{#each context in controller.sortColumns}}
<th><a {{action sortby context href=true target="controller"}}>{{context.column}}</a></th>
{{/each}}
</thead>
<tbody>
   {{#each person in controller.paginatedContent}}
    <tr>
      <td>{{person.id}}</td>
      <td>{{view Ember.TextField valueBinding="person.username"}}</td>
    </tr>
   {{/each}}
</tbody>
</table>

<div name="prev"><a class="paginator" {{action prevPage href=true target="controller"}}>Prev</a></div>
<ul class="pagination gui-text">
  {{#each pages}}
    {{view PersonApp.PaginationItemView contentBinding="this"}}
  {{/each}}
</ul>
<div name="next"><a class="paginator" {{action nextPage href=true target="controller"}}>Next</a></div>
</script>

<script type="text/x-handlebars" data-template-name="pagination_item">
{{#with view}}
<a {{action paginateUsers content href=true}}>
  <span {{bindAttr class="spanClasses isActive:active"}}>{{content.page_id}}</span>
</a>
{{/with}}
</script>
    </body>
</html>

JavaScript

var get = Ember.get;

Ember.SortablePaginationMixin = Ember.Mixin.create({

  filterBy: '',
  sortDirection: 'asc',

  availableColumns: function() {
      //hack for jsfiddle to work ...
      return ["id","username"];
      //return this.get('content').get('type.ClassMixin.ownerConstructor.attributes.keys').toArray();
  }.property(),

  sortColumns: function() {

    var sortable = [];
    var attributes = this.get('availableColumns');
    for (var i = 0; i < attributes.length; i++) {
      sortable.push({ column: attributes[i], dir: 'asc' });
    }
    return sortable;

  }.property(),

  sortby: function(event) {
    var sortby = event.context.column;
    var incomingDirection = event.context.dir;
    var route = this.get('sortableRoute');
    var currentSortBy = this.get('sortBy');
    var currentSortDirection = this.get('sortDirection');
    var direction = 'asc';

    if (sortby === currentSortBy) {
      if (incomingDirection === currentSortDirection) {
        var direction = 'desc';
      }
    }

    this.get('target').send(route, {column:sortby,dir:direction});
  },

  pages: function() {

    var availablePages = this.get('availablePages'),
    pages = [],
    page;

    for (i = 0; i < availablePages; i++) {
      page = i + 1;
      pages.push({ page_id: page.toString() });
    }

    return pages;

  }.property('availablePages'),

  currentPage: function() {

    return parseInt(this.get('selectedPage'), 10) || 1;

  }.property('selectedPage'),

  nextPage: function() {

    var nextPage = this.get('currentPage') + 1;
    var availablePages = this.get('availablePages');
    if (nextPage <= availablePages) { this.transitionTo(nextPage) }

  },

  prevPage: function() {

    var prevPage = this.get('currentPage') - 1;
    if (prevPage > 0) { this.transitionTo(prevPage) }

  },

  transitionTo: function(page) {

    var pages = this.get('pages');
    var route = this.get('paginationRoute');
    this.get('target').send(route, pages[page - 1]);

  },

...