knockout.js クリックされた行をinputタグに置き換える

2012/04/29
ifバインディングを使用すれば良い。

めもとサンプルソース

経緯:第一弾

とりあえず、最初からinputタグにしておく。
inputタグにはCSSを設定して線とかを消しておく。
このままだと線の無いinputなので編集をできなくしておく。
で、クリックされたら線とかを消しているCSSを削除して編集をできるようにする。

というような考えでやってみる。

サンプル:
ボタンクリックでinputタグに切替(enableバインディング)

  1. <!DOCTYPE html>
  2. <html lang="ja">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>ボタンクリックでinputタグに切替(enableバインディング)</title>
  6. <script type="text/javascript" src="jquery-1.5.1.min.js"></script>
  7. <script type="text/javascript" src="knockout-2.1.0beta.js"></script>
  8.  
  9. <script type="text/javascript">
  10.   $(document).ready(function () {
  11.     function viewModel() {
  12.       var self = this;
  13.  
  14.       self.tourism = ko.observableArray();
  15.  
  16.       // 表示したいデータ:とりあえずコメントアウトしておく。
  17.       self.tourism([
  18.         { local: '九州地方', area: '太宰府', kind: '歴史遺産', description: '太宰府天満宮、大宰府政庁跡。', },
  19.         { local: '東北地方', area: '大仙市', kind: '文化・自然・温泉', description: '大曲花火大会、秋ノ宮温泉郷など。', },
  20.         { local: '北海道', area: '七飯', kind: '自然・保養', description: '大沼・小沼などの大沼国定公園、流山温泉。', },
  21.         { local: '沖縄地方', area: '恩納', kind: '保養地・自然', description: 'ビーチリゾート、万座毛。', },
  22.         { local: '北海道', area: '旭川', kind: '文化', description: 'アイヌ文化、旭山動物園など。', },
  23.         { local: '九州地方', area: '指宿', kind: '保養・自然', description: '指宿温泉、開聞岳、池田湖、長崎鼻。', },
  24.         { local: '九州地方', area: '鹿児島', kind: '歴史遺産・自然', description: '桜島、磯庭園、城山など。', },
  25.         { local: '関東地方', area: '箱根', kind: '保養', description: '箱根温泉、芦ノ湖、箱根駅伝。', },
  26.         { local: '中部地方', area: '黒部', kind: '自然・温泉', description: '宇奈月温泉などの温泉、黒部峡谷。', },
  27.         { local: '中部地方', area: '名古屋', kind: '歴史遺産・文化', description: '名古屋城、熱田神宮、徳川美術館', },
  28.         { local: '四国地方', area: '大洲', kind: '町並・歴史遺産', description: 'おはなはん通り、大洲城、鵜飼など。', },
  29.         { local: '中国地方', area: '米子', kind: '保養・歴史遺産', description: '皆生温泉、米子城跡と城下町', },
  30.         { local: '中国地方', area: '出雲市', kind: '自然・歴史遺産', description: '出雲大社、日御碕、立久恵峡など。', },
  31.         { local: '沖縄地方', area: '南城市知念地区', kind: '歴史遺産', description: '世界遺産である斎場御嶽。', },
  32.       ]);
  33.  
  34.       // データとは別にいくつか追加
  35.       for (var i in self.tourism()) {
  36.         // enableの追加:初期値として編集不可
  37.         self.tourism()[i].editDataTourism = ko.observable(false);
  38.  
  39.         // 編集ボタンの初期値
  40.         self.tourism()[i].btnLabel = ko.observable("編集");
  41.       }
  42.  
  43.       // 「編集」ボタンが押されたとき
  44.       self.gotoEditTourism = function(targetData) {
  45.         // 他を編集不可にしておく。
  46.         for (var num in self.tourism()) {
  47.           // 自分自身じゃなければ色々といじる。
  48.           if ( targetData.editDataTourism() != true ) {
  49.             self.tourism()[num].editDataTourism(false);
  50.             self.tourism()[num].btnLabel("編集");
  51.           }
  52.         }
  53.  
  54.         // inputの編集の可・不可
  55.         targetData.editDataTourism( !targetData.editDataTourism() );
  56.  
  57.         // 編集ボタンの変更
  58.         if ( targetData.editDataTourism() == true ) {
  59.           targetData.btnLabel("確定");
  60.         }
  61.         else {
  62.           targetData.btnLabel("編集");
  63.         }
  64.       };
  65.  
  66.     }
  67.  
  68.     ko.applyBindings(new viewModel());
  69.   });
  70. </script>
  71.  
  72. <style type="text/css">
  73. input[type=text]{
  74.   background-color:#ffffdd;
  75. }
  76.  
  77. input[type=text].edit{
  78.   border:none;
  79.   color:#000;
  80.   background-color:#fff;
  81. }
  82. </style>
  83.  
  84. </head>
  85. <body>
  86. <h1>ボタンクリックでinputタグに切替(enableバインディング)</h1>
  87.  
  88. <table>
  89. <thead>
  90. <tr>
  91. <th>地方</th>
  92. <th>地域</th>
  93. <th>種別</th>
  94. <th>説明</th>
  95. <th>-</th>
  96. </tr>
  97. </thead>
  98. <tbody data-bind="foreach: tourism, visible: tourism">
  99. <tr>
  100. <td><input type="text" data-bind="css:{'edit': editDataTourism() == false}, value: local, enable: editDataTourism" /></td>
  101. <td><input type="text" data-bind="css:{'edit': editDataTourism() == false}, value: area, enable: editDataTourism" /></td>
  102. <td><input type="text" data-bind="css:{'edit': editDataTourism() == false}, value: kind, enable: editDataTourism" /></td>
  103. <td><input type="text" data-bind="css:{'edit': editDataTourism() == false}, value: description, enable: editDataTourism" /></td>
  104. <td><button data-bind="text: btnLabel, click: $root.gotoEditTourism"></button></td>
  105. </tr>
  106. </tbody>
  107. </table>
  108.  
  109. </body>
  110. </html>

サンプルソースの35~40行目でフラグ替わりに「editDataTourism」って名前で追加しておいた。
あとは、「編集」ボタンを押したら「確定」ボタンと表示を変えたいのでこれも別途追加してみた。

104行目のclickイベントで44~66行目の「self.gotoEditTourism」を実行。

「編集ボタン」を押したあとに他の行の「編集ボタン」を押した場合の対応をするために46~52行目でクリックされていない行を変更する。

あとは54~63行目でcss・enableバインディングのためにtrueとfalseの切替とかする。

経緯:第二弾

enableバインディングだと文字の表示はされるけど、マウスで選択ができない。
コレはユーザビリティ的にダメじゃん。それにinputタグの幅を可変にする何かを用意しないと…やっぱりユーザビリティ的にダメじゃんってことで別の方法を考えることにする。
ということでifバインディングでtemplateバインディングを切り分けよう。
その前に、ifバインディングだけで試してみよう。
サンプル:
ボタンクリックでinputタグに切替(ifバインディング)

  1. <!DOCTYPE html>
  2. <html lang="ja">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>ボタンクリックでinputタグに切替(ifバインディング)</title>
  6. <script type="text/javascript" src="jquery-1.5.1.min.js"></script>
  7. <script type="text/javascript" src="knockout-2.1.0beta.js"></script>
  8.  
  9. <script type="text/javascript">
  10.   $(document).ready(function () {
  11.     function viewModel() {
  12.       var self = this;
  13.  
  14.       self.tourism = ko.observableArray();
  15.  
  16.       // 表示したいデータ:とりあえずコメントアウトしておく。
  17.       self.tourism([
  18.         { local: '九州地方', area: '太宰府', kind: '歴史遺産', description: '太宰府天満宮、大宰府政庁跡。', },
  19.         { local: '東北地方', area: '大仙市', kind: '文化・自然・温泉', description: '大曲花火大会、秋ノ宮温泉郷など。', },
  20.         { local: '北海道', area: '七飯', kind: '自然・保養', description: '大沼・小沼などの大沼国定公園、流山温泉。', },
  21.         { local: '沖縄地方', area: '恩納', kind: '保養地・自然', description: 'ビーチリゾート、万座毛。', },
  22.         { local: '北海道', area: '旭川', kind: '文化', description: 'アイヌ文化、旭山動物園など。', },
  23.         { local: '九州地方', area: '指宿', kind: '保養・自然', description: '指宿温泉、開聞岳、池田湖、長崎鼻。', },
  24.         { local: '九州地方', area: '鹿児島', kind: '歴史遺産・自然', description: '桜島、磯庭園、城山など。', },
  25.         { local: '関東地方', area: '箱根', kind: '保養', description: '箱根温泉、芦ノ湖、箱根駅伝。', },
  26.         { local: '中部地方', area: '黒部', kind: '自然・温泉', description: '宇奈月温泉などの温泉、黒部峡谷。', },
  27.         { local: '中部地方', area: '名古屋', kind: '歴史遺産・文化', description: '名古屋城、熱田神宮、徳川美術館', },
  28.         { local: '四国地方', area: '大洲', kind: '町並・歴史遺産', description: 'おはなはん通り、大洲城、鵜飼など。', },
  29.         { local: '中国地方', area: '米子', kind: '保養・歴史遺産', description: '皆生温泉、米子城跡と城下町', },
  30.         { local: '中国地方', area: '出雲市', kind: '自然・歴史遺産', description: '出雲大社、日御碕、立久恵峡など。', },
  31.         { local: '沖縄地方', area: '南城市知念地区', kind: '歴史遺産', description: '世界遺産である斎場御嶽。', },
  32.       ]);
  33.  
  34.       // データとは別にいくつか追加
  35.       for (var i in self.tourism()) {
  36.         // enableの追加:初期値として編集不可
  37.         self.tourism()[i].editDataTourism = ko.observable(false);
  38.  
  39.         // 編集ボタンの初期値
  40.         self.tourism()[i].btnLabel = ko.observable("編集");
  41.       }
  42.  
  43.       // 「編集」ボタンが押されたとき
  44.       self.gotoEditTourism = function(targetData) {
  45.         // 他を編集不可にしておく。
  46.         for (var num in self.tourism()) {
  47.           // 自分自身じゃなければ色々といじる。
  48.           if ( targetData.editDataTourism() != true ) {
  49.             self.tourism()[num].editDataTourism(false);
  50.             self.tourism()[num].btnLabel("編集");
  51.           }
  52.         }
  53.  
  54.         // inputの編集の可・不可
  55.         targetData.editDataTourism( !targetData.editDataTourism() );
  56.  
  57.         // 編集ボタンの変更
  58.         if ( targetData.editDataTourism() == true ) {
  59.           targetData.btnLabel("確定");
  60.         }
  61.         else {
  62.           targetData.btnLabel("編集");
  63.         }
  64.       };
  65.  
  66.     }
  67.  
  68.     ko.applyBindings(new viewModel());
  69.   });
  70. </script>
  71.  
  72. <style type="text/css">
  73. input[type=text]{
  74.   background-color:#ffffdd;
  75. }
  76.  
  77. input[type=text].edit{
  78.   color:#000;
  79.   background-color:#fff;
  80. }
  81. </style>
  82.  
  83. </head>
  84. <body>
  85. <h1>ボタンクリックでinputタグに切替(ifバインディング)</h1>
  86.  
  87. <table>
  88. <thead>
  89. <tr>
  90. <th>地方</th>
  91. <th>地域</th>
  92. <th>種別</th>
  93. <th>説明</th>
  94. <th>-</th>
  95. </tr>
  96. </thead>
  97. <tbody data-bind="foreach: tourism, visible: tourism">
  98. <tr data-bind="if: editDataTourism() == false">
  99. <td data-bind="text: local"></td>
  100. <td data-bind="text: area"></td>
  101. <td data-bind="text: kind"></td>
  102. <td data-bind="text: description"></td>
  103. <td><button data-bind="text: btnLabel, click: $root.gotoEditTourism"></button></td>
  104. </tr>
  105. <tr data-bind="if: editDataTourism() == true">
  106. <td><input type="text" data-bind="css:{'edit': editDataTourism() == false}, value: local" /></td>
  107. <td><input type="text" data-bind="css:{'edit': editDataTourism() == false}, value: area" /></td>
  108. <td><input type="text" data-bind="css:{'edit': editDataTourism() == false}, value: kind" /></td>
  109. <td><input type="text" data-bind="css:{'edit': editDataTourism() == false}, value: description" /></td>
  110. <td><button data-bind="text: btnLabel, click: $root.gotoEditTourism"></button></td>
  111. </tr>
  112. </tbody>
  113. </table>
  114.  
  115. </body>
  116. </html>

ifバインディングの使い方を参考にやってみた。
98~104行目と105~111行目を条件によって切り替える。
※プログラムとかのif文でのelseみたいなのがあればいいのに…。

経緯:第三弾

ifバインディングで成功したのでtemplateバインディングも追加してみた。
サンプル:
ボタンクリックでinputタグに切替(ifバインディング)

  1. <!DOCTYPE html>
  2. <html lang="ja">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>ボタンクリックでinputタグに切替(ifバインディング)</title>
  6. <script type="text/javascript" src="jquery-1.5.1.min.js"></script>
  7. <script type="text/javascript" src="knockout-2.1.0beta.js"></script>
  8.  
  9. <script type="text/javascript">
  10.   $(document).ready(function () {
  11.     function viewModel() {
  12.       var self = this;
  13.  
  14.       self.tourism = ko.observableArray();
  15.  
  16.       // 表示したいデータ:とりあえずコメントアウトしておく。
  17.       self.tourism([
  18.         { local: '九州地方', area: '太宰府', kind: '歴史遺産', description: '太宰府天満宮、大宰府政庁跡。', },
  19.         { local: '東北地方', area: '大仙市', kind: '文化・自然・温泉', description: '大曲花火大会、秋ノ宮温泉郷など。', },
  20.         { local: '北海道', area: '七飯', kind: '自然・保養', description: '大沼・小沼などの大沼国定公園、流山温泉。', },
  21.         { local: '沖縄地方', area: '恩納', kind: '保養地・自然', description: 'ビーチリゾート、万座毛。', },
  22.         { local: '北海道', area: '旭川', kind: '文化', description: 'アイヌ文化、旭山動物園など。', },
  23.         { local: '九州地方', area: '指宿', kind: '保養・自然', description: '指宿温泉、開聞岳、池田湖、長崎鼻。', },
  24.         { local: '九州地方', area: '鹿児島', kind: '歴史遺産・自然', description: '桜島、磯庭園、城山など。', },
  25.         { local: '関東地方', area: '箱根', kind: '保養', description: '箱根温泉、芦ノ湖、箱根駅伝。', },
  26.         { local: '中部地方', area: '黒部', kind: '自然・温泉', description: '宇奈月温泉などの温泉、黒部峡谷。', },
  27.         { local: '中部地方', area: '名古屋', kind: '歴史遺産・文化', description: '名古屋城、熱田神宮、徳川美術館', },
  28.         { local: '四国地方', area: '大洲', kind: '町並・歴史遺産', description: 'おはなはん通り、大洲城、鵜飼など。', },
  29.         { local: '中国地方', area: '米子', kind: '保養・歴史遺産', description: '皆生温泉、米子城跡と城下町', },
  30.         { local: '中国地方', area: '出雲市', kind: '自然・歴史遺産', description: '出雲大社、日御碕、立久恵峡など。', },
  31.         { local: '沖縄地方', area: '南城市知念地区', kind: '歴史遺産', description: '世界遺産である斎場御嶽。', },
  32.       ]);
  33.  
  34.       // データとは別にいくつか追加
  35.       for (var i in self.tourism()) {
  36.         // enableの追加:初期値として編集不可
  37.         self.tourism()[i].editDataTourism = ko.observable(false);
  38.  
  39.         // 編集ボタンの初期値
  40.         self.tourism()[i].btnLabel = ko.observable("編集");
  41.       }
  42.  
  43.       // 「編集」ボタンが押されたとき
  44.       self.gotoEditTourism = function(targetData) {
  45.         // 他を編集不可にしておく。
  46.         for (var num in self.tourism()) {
  47.           // 自分自身じゃなければ色々といじる。
  48.           if ( targetData.editDataTourism() != true ) {
  49.             self.tourism()[num].editDataTourism(false);
  50.             self.tourism()[num].btnLabel("編集");
  51.           }
  52.         }
  53.  
  54.         // inputの編集の可・不可
  55.         targetData.editDataTourism( !targetData.editDataTourism() );
  56.  
  57.         // 編集ボタンの変更
  58.         if ( targetData.editDataTourism() == true ) {
  59.           targetData.btnLabel("確定");
  60.         }
  61.         else {
  62.           targetData.btnLabel("編集");
  63.         }
  64.       };
  65.  
  66.     }
  67.  
  68.     ko.applyBindings(new viewModel());
  69.   });
  70. </script>
  71.  
  72. <style type="text/css">
  73. input[type=text]{
  74.   background-color:#ffffdd;
  75. }
  76.  
  77. input[type=text].edit{
  78.   color:#000;
  79.   background-color:#fff;
  80. }
  81. </style>
  82.  
  83. </head>
  84. <body>
  85. <h1>ボタンクリックでinputタグに切替(ifバインディング)</h1>
  86.  
  87. <table>
  88. <thead>
  89. <tr>
  90. <th>地方</th>
  91. <th>地域</th>
  92. <th>種別</th>
  93. <th>説明</th>
  94. <th>-</th>
  95. </tr>
  96. </thead>
  97. <tbody data-bind="foreach: tourism, visible: tourism">
  98. <!-- ko if: editDataTourism() == false -->
  99. <tr data-bind="template: {name: 'viewTourism'}"></tr>
  100. <!-- /ko -->
  101. <!-- ko if: editDataTourism() == true -->
  102. <tr data-bind="template: {name: 'editTourism'}"></tr>
  103. <!-- /ko -->
  104. </tbody>
  105. </table>
  106.  
  107. <script type="text/html" id="viewTourism">
  108.   <td data-bind="text: local"></td>
  109.   <td data-bind="text: area"></td>
  110.   <td data-bind="text: kind"></td>
  111.   <td data-bind="text: description"></td>
  112.   <td><button data-bind="text: btnLabel, click: $root.gotoEditTourism"></button></td>
  113. </script>
  114.  
  115. <script type="text/html" id="editTourism">
  116. <td><input type="text" css="edit" data-bind="value: local" /></td>
  117. <td><input type="text" css="edit" data-bind="value: area" /></td>
  118. <td><input type="text" css="edit" data-bind="value: kind" /></td>
  119. <td><input type="text" css="edit" data-bind="value: description" /></td>
  120. <td><button data-bind="text: btnLabel, click: $root.gotoEditTourism"></button></td>
  121. </script>
  122.  
  123. </body>
  124. </html>

予定では「trタグ」内でifバインディングとtemplateバインディングを記述したかった。
そういうのはNGらしいからtrタグの外にコメントタグを置いてifバインディング(98・101行目)してみた。
あとは、テンプレートは107~113行目と115~121行目にある。

ひとりごと

knockout.jsをやっていて思うのがhtml-lintでいっぱいエラー出てきそう。
現状のSEOには不向きだな。一応、GoogleはJavascriptも無問題とか言ってるけど今一つ信用できない。

新着(ニュース関連以外)

2018-07-26
年賀状で「新春」とか書くけど・・・何故なんだろうと8月を目前にした今、疑問に思った。
2018-05-16
PHPで画像のヘッダ情報(?)の「Orientation」を元に画像回転させたい。
2018-03-05
Android Studioをインストール。エミュレータを軽くするトコまで終わらせたかったけど、挫折した。
2018-02-23
プッシュ通知について調べてた時にでてきたServiceWorker。そのServiceWorkerについてのメモ。
2017-12-13
jqueryで取得したDOM要素をオブジェクトじゃなくて、配列で受け取りたい