4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Knockout.js(+Electron)でTODOアプリ作成してみた

Last updated at Posted at 2017-09-04

お仕事で Knockout.js + Electron を触る可能性があるので
まずはチュートリアルってことで TODOアプリ を作成してみました

Knockout.jsとは

Githubリポジトリ
日本語ドキュメント

Knockout.jsは以下の特徴を持ったMVVMライブラリです

  • エレガントな依存トラッキング
  • 宣言型 バインディング
  • 拡張が容易

他のMVVMに比べて非常にシンプルでわかりやすい、というのが第一印象です

インストール

公式サイトでは <script> タグでの読み込みが紹介されていますが
今回は Electron で動かすので npm モジュールとしてインストールします

$ npm install --save knockout

ついでに electron webpack webpack-dev-server` をインストールします

$ npm install --save electron webpack webpack-dev-server

webpackの設定

webpackの設定を以下のようにします

webpack.config.js
const path = require('path');

module.exports = {
  context: path.resolve(__dirname, './src'),
  entry: './index.js',
  output: {
    filename: 'bundle.js',
    // webとelectron両方で動かすため path と publicPath を同じ値にしておく
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'dist'
  },

  devServer: {
    contentBase: path.resolve(__dirname, './src'),
    port: 3000,
  },

  // electron用の設定??
  target: 'electron-renderer'
};

今回はwebとelectron両方で動かしたかったので
pathとpublicPathの値を同じパスにしました。

target の値を electron-renderer にすると
electron用にビルドしてくれる?みたいですが正直よくわかりませんでした。。。

またpackage.jsonにもコマンドを追加しておきます

package.json
  "scripts": {
    "webpack": "webpack-dev-server --hot --inline --target web",
    "electron": "webpack && electron ."
  },

UI

TODOアプリの見た目はこちらのサイトからお借りしました

src/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
    <link rel="stylesheet" href="style.css">
    <script src="../dist/bundle.js"></script>
  </head>
  <body>
    <div class="header">
        <h2>My To Do List</h2>
        <input type="text" placeholder="Title..." data-bind="value: taskSummary">
        <span class="addBtn" data-bind="click: addTask">Add</span>
      </div>
      
      <ul data-bind="foreach: todoList">
        <li data-bind="text: summary, css: {'checked': isDone}, click: $root.toggleDone"></li>
      </ul>
  </body>
</html>

data-bind属性の部分が knockout.jsのデータバインドを宣言している部分となります
シンプルでわかりやすいですね:sparkles:

ロジック

src/index.js
const ko = require('knockout')

function Viewmodel() {
    let self = this

    self.todoList = ko.observableArray([
        {summary: "hogehoge", isDone: true},
        {summary: "hogehoge", isDone: true},
        {summary: "hogehoge3", isDone: true}
    ])

    self.toggleDone = (task) => {
        let updateTask = {
            summary: task.summary,
            isDone: !task.isDone
        }

        self.todoList.replace(task, updateTask)
    }

    self.taskSummary = ko.observable('')
    self.addTask = _ => {
        let newTask = {
            summary: self.taskSummary(),
            isDone: false
        }
        
        self.todoList.push(newTask)
        self.taskSummary('')
    }
}

window.onload = _ => {
    ko.applyBindings(new Viewmodel())
}

Viewmodelを作成し、そちらを ko.applyBindings に渡しています。
ES2015で書くんだったら class で作成したほうがよかったかもしれないです

ko.applyBindings 関数は DOMが準備された後に呼び出さないとエラーになるため
windows.onloadの中で読んでいます

Viewmodelではプロパティの値を ovservable observableArray でラッピングしています
こうすることでプロパティの更新をknocokout.jsが検知し、UIを変更します

observavleArrayの特定の要素のみ更新する方法でちょっとハマりましたが
observableArray#replace 関数を使うことで特定の要素のみ更新できるようです

参考: https://stackoverflow.com/questions/8774943/knockoutjs-observablearray-to-update-when-inner-observable-is-changed

electron

Electronで動かすための main.js を書きます

src/main.js
// electronモジュールを読み込み
const electron = require('electron');
const {app} = electron;
const {BrowserWindow} = electron; //ウィンドウを表す[BrowserWindow]はelectronモジュールに含まれている

// 新しいウィンドウ(Webページ)を生成
let win;
function createWindow() {
  // BrowserWindowインスタンスを生成
  win = new BrowserWindow({width: 800, height: 600});
  // index.htmlを表示
  win.loadURL(`file://${__dirname}/src/index.html`);
  // デバッグするためのDevToolsを表示
  win.webContents.openDevTools();
  // ウィンドウを閉じたら参照を破棄
  win.on('closed', () => {   // ()は function ()と書いていい
    win = null;
  });
}
// アプリの準備が整ったらウィンドウを表示
app.on('ready', createWindow);
// 全てのウィンドウを閉じたらアプリを終了
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
app.on('activate', () => {
  if (win === null) {
    createWindow();
  }
});

ネットに落ちていたサンプルコードをそのままコピペしただけになります。。。

作成したもの

デモ: https://tamanugi.github.io/knockout_electron/src/index.html
リポジトリ: https://github.com/tamanugi/knockout_electron

# 感想とか

とてもシンプルでとっつきやすいライブラリだと感じました。
他のMVVMとくらべても覚えることが少なく、非常にわかりやすいです

どちらかというとWebpackとelctronについて勉強しないとだめですね。。。(webpackムズカシイ

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?