JSFiddle - React, Tailwind, and code Playground

by sfpgmr

HTML

<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
<script src="http://sfpgmr.github.io/0002/scripts/undoredo.js"></script>
<button data-bind="click:addRecord" >追加</button>
<table class="table table-striped table-condensed">
    <thead>
        <tr>
            <th><a href="#" data-bind="click:sortName" >名前</a></th>
            <th><a href="#" data-bind="click:sortAge" >年齢</a></th>
            <th><a href="#" data-bind="click:sortHobby" >趣味</a></th>
        </tr>
    </thead>
    <tbody data-bind="foreach:data">
        <tr>
            <td><input type="text" data-bind="value:name"/></td>
            <td><input type="text" data-bind="value:age"/></td>
            <td><select data-bind="options: $root.hobbies,value:hobby"></select></td>
            <td><button data-bind="click:$parent.deleteRecord">削除</button></td>
        </tr>
    </tbody>
</table>

JavaScript

function ViewModel()
{
    var self = this;
    var dirName = false;
    var dirAge = false;
    var dirHobby = false;
    self.hobbies = ['野球','サッカー','卓球','ゴルフ','バレー','バスケットボール','映画','PC'];
    self.data = ko.observableArray(
        [
            {
                name:ko.observable('田中'),
                age:ko.observable(18),
                hobby:ko.observable('ゴルフ')
            },
            {
                name:ko.observable('鈴木'),
                age:ko.observable(17),
                hobby:ko.observable('野球')
            },
            {
                name:ko.observable('佐藤'),
                age:ko.observable(37),
                hobby:ko.observable('サッカー')
            }
        ]); 

    self.sortName = function()
    {
        dirName= !dirName;
        self.data.sort(function(a,b){
            if(dirName){
                return a.name() > b.name();
            } else {
                return a.name() < b.name();
            }
        });
    };

    self.sortHobby = function()
    {
        dirHobby= !dirHobby;
        self.data.sort(function(a,b){
            if(dirHobby){
                return a.hobby() > b.hobby();
            } else {
                return a.hobby() < b.hobby()
            }
        });
    };

    self.sortAge = function()
    {
        dirAge= !dirAge;
        self.data.sort(function(a,b){
            if(dirAge){
                return a.age() > b.age();
            } else {
                return a.age() < b.age();
            }
        });
    };
    
    self.addRecord = function()
   {
       self.data.push({
           name:ko.observable('新規'),
           age:ko.observable(),
           hobby:ko.observable()
       });
   };
   
   self.deleteRecord = function(target){
       self.data.remove(target);
   };
}

$().ready(function(){
    ko.applyBindings(new ViewModel());
});