SPAフレームワークLighterの紹介

# Lighterとは
Lighterは僕が開発したLaravelとVue.jsベースのSPAフレームワークです。

# なぜ作ったか
最近LaravelとVue.jsでSPAを作っています。
その中で「Laravelとの通信の部分が増えてきてコードが見にくくなっている」と思いました。
もうちょっとリーダブルなコードがかけないものか...

せや、フレームワークつくろ。
（現時点ではフレームワークと言っていいのかわかりません。）

# Lighterの紹介
実際にLighterでTODOアプリを作りながら解説していきます。
良ければ一緒に進めてみてください。

※注意：この記事では基本的に`yarn`を使っていきます。

## ダウンロードとセットアップ
[こちら](https://github.com/lighter-framework/lighter)からzipでダウンロードしてください。
ダウンロードしたら解凍してください。
ターミナルかコマンドプロンプトで解凍したフォルダに移動してください。
そこで最初に

```shell
composer install
```
を実行してください。
次に`npm`を使っている方は

```shell
npm install
npm run lighter:setup
```
を実行してください。
`yarn`を使っている方は

```shell
yarn
yarn run lighter:setup
```
を実行してください。

## サーバーの起動
HMRサーバーとPHPサーバーの起動を行います。

### 最初にHMRサーバーの起動。
`npm`を使っている方は

```shell
npm run hot
```
を実行してください。
`yarn`を使っている方は

```shell
yarn run hot
```
を実行してください。

### 次にPHPサーバーの起動
新しいターミナルかコマンドプロンプトを開き

```shell
php artisan serve
```
を実行してください。

これで<http://127.0.0.1:8000/>を開き
![スクリーンショット 2018-07-11 10.59.08.png](https://qiita-image-store.s3.amazonaws.com/0/154299/ea57c6c0-1667-6e0a-e5d2-fb17c453ab57.png)
が表示されていたら成功です。

# TODOアプリの作成
機能としては最低限の
1. タスクの一覧表示
2. タスクの作成
3. ~~タスクの削除~~今回は長くなってしまうので作りません。
4. ~~タスクの並び替え~~今回は長くなってしまうので作りません。
4. タスクの編集
5. タスクの「完了・未完了」の切り替え
にします。
見た目は最近気にっている[vuetify](vuetifyjs.com/ja)を使います。

## 設定
今回のTODOアプリはデータベースが必要なので１つ作っておいてください。
名前はご自由に。
まず`.env`ファイルを開いてください。
ここでデータベースの設定をしてください。
僕の場合はMAMPを使っているので

```ini:.env
APP_NAME=LighterTodo
APP_ENV=local
APP_KEY=# App key here
APP_DEBUG=true
APP_URL=http://localhost

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock
DB_DATABASE=lighter_todo_demo
DB_USERNAME=root
DB_PASSWORD=root

BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_LIFETIME=120
QUEUE_DRIVER=sync

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1

MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

```

## Vuetifyのインストール
```
yarn add vuetify
```
でインストールします。

次に`resources/assets/js/app.js`を開いてください。
最初は

```app.js
import Vue from 'vue'

import router from './router'
import store from './store'
import Lighter from './lighter'

import '../sass/app.scss'

Vue.component('app', require('@/layouts/App'))

const app = new Vue({
    router,
    store,
}).$mount('#app')
```
こんな内容になっていると思います。
これを

```app.js
import Vue from 'vue'

import Vuetify from 'vuetify' //Vuetifyのインポート
import '../../../node_modules/vuetify/dist/vuetify.min.css' //VuetifyのCSSの読み込み

import router from './router'
import store from './store'
import Lighter from './lighter'

import '../sass/app.scss'

Vue.use(Vuetify) //Vuetifyを有効化

Vue.component('app', require('@/layouts/App'))

const app = new Vue({
    router,
    store,
}).$mount('#app')
```
このようにします。
これでVuetfiyが使える状態になりました。

## MaterialDesignIconsを読み込む
`resources/views/app.blade.php`を開いてください。
内容を以下のようにしてください。

```html:app.blade.php
<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet">

    <title>Lighter</title>

    @include('Lighter.HeadScript')
</head>
<body>
<div id="app">
    <app></app>
</div>
<script src="{{mix('js/app.js')}}"></script>
</body>
</html>
```

## 不要なマイグレーションの削除
まず、LaravelAuthのマイグレーションを削除します。
`database/migrations`フォルダを空にしてください。

## `Task`モデルと`tasks`テーブルの作成
`php artisan make:model Task -m`を実行し`Task`モデルと`tasks`テーブルのマイグレーションを作成します。
`database/migrations`に`tasks`テーブルのマイグレーションが作成されていることを確認してください。
'tasks`テーブルのマイグレーションの内容を以下のようにします。

```php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTasksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('order');
            $table->string('name');
            $table->boolean('is_finished');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
}
```

1. `order`カラムはタスクの順番
2. `name`カラムはタスクの名前
3. `is_finished`カラムはタスクの完了・未完了（trueなら完了、falseなら未完了）

次に`Task`モデルの準備をします。
`app/Task.php`を以下のようにします。

```Task.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    protected $table = 'tasks';
}
```

## 全体のレイアウトを決める
`resources/assets/js/layouts`フォルダを見てください。
このフォルダはアプリケーションのレイアウトコンポーネントを入れるフォルダです。
基本的なレイアウトは`App.vue`ファイルに記述します。
`App.vue`を以下のようにします。

```App.vue
<template>
    <v-app>
        <v-toolbar color="blue" dark>
            <v-toolbar-title>
                Lighter TODO
            </v-toolbar-title>
        </v-toolbar>
        <v-content>
            <v-container>
                <router-view></router-view>
            </v-container>
        </v-content>
    </v-app>
</template>

<script>
    export default {
        name: "app",
    }
</script>

<style scoped>
</style>
```

## TaskListページの作成
`resources/assets/js/pages`フォルダを見てください。
このフォルダはアプリケーションのページコンポーネントを入れるフォルダです。
初期には`Example.vue`ファイルが入っています。
このファイルは不要なので削除します。

次に`TaskList.vue`ファイルを作成してください。
試しに内容を以下のようにしてみます。

```TaskList.vue
<template>
    <div>
        <h1 class="display-3">TaskList</h1>
    </div>
</template>

<script>
export default {
    name: 'TaskList',
}
</script>

<style>

</style>
```

最後にルーターに登録します。
`resources/assets/js/router`フォルダを見てください。
このフォルダには`index.js`と`routes.js`ファイルがあります。
`index.js`にはルーターの定義が予め書いてあります。
`routes.js`にはルートの定義を書きます。

`routes.js`を開き、以下のようにします。

```routes.js
const routes = [
    {
        path: '/',
        component: require('@/pages/TaskList')
    }
]

export default routes
```

そして
![スクリーンショット 2018-07-11 12.55.42.png](https://qiita-image-store.s3.amazonaws.com/0/154299/d7659fba-3940-720c-4da8-c2cd38ea7197.png)
このように表示されてれば正常です。

## CreateTaskページの作成
`resouces/assets/js/pages`フォルダに`CreateTask.vue`ファイルを作成してください。
内容は以下のようにしてください。

```CreateTask.vue
<template>
    <div>
        <h1 class="display-3">CreateTask</h1>
        <v-card>
            <v-card-text>
                <v-text-field label="Name" v-model="name"></v-text-field>
                <v-btn color="primary" @click="create">Create</v-btn>
            </v-card-text>
        </v-card>
    </div>
</template>

<script>
export default {
    name: 'CreateTask',
    data () {
        return {
            name: '',
        }
    },
    methods: {
        create () {
            
        }
    }
}
</script>

<style>

</style>

```

<http://127.0.0.1:8000/create>にアクセスし

![スクリーンショット 2018-07-11 13.16.15.png](https://qiita-image-store.s3.amazonaws.com/0/154299/954eb801-1cfd-cb13-d1f7-eaf87458168f.png)
このように表示されていれば正常です。

## Sparkの簡単な説明
Lighterには`Spark`という機能があります。
`Spark`はLaravelとの通信をより分かりやすくするためのラッパーです。
例えば今回の`tasks`テーブルに対応する`Spark`を作ると以下のようなプログラムで`tasks`テーブルにレコードをインサートすることができます。

```javascript

Tasks.create({
    name: 'Create a project.'
})
```

セキュリティー面はLaravel側で行うのでフロント側では最低限のバリデーションをもたせます。

## TasksSparkの作成
`resources/assets/js/sparks`フォルダを見てください。
このフォルダに各通信やテーブル操作の`spark`を作成していきます。

今回は`tasks`テーブルに対応する`spark`を作るので`Tasks.js`を作成します。

まず`Spark`を`import`します。


```Tasks.js
import Spark from '../lighter/spark'
```
次に`Tasks`オブジェクトを作ります。

```Tasks.js
import Spark from '../lighter/spark'

var Tasks = new Spark('tasks') // Tasksオブジェクトの作成
```

このように`Spark`はクラスになっていて、コンストラクタにルートプレフィックス（後記述）を渡します。

### 対応するルートの作成
`Spark`は各操作に対応するルートにリクエストすることでデータを取得します。
`routes/web.php`を開いてください。

中身が以下のようになっていると思います。

```web.php
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::prefix('local_api')->group(function () {
    /*
     * Local API route here.
     */
});

Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');

```
この

```php

Route::prefix('local_api')->group(function () {
    /*
     * Local API route here.
     */
});
```
の部分にルートを定義します。
また、先程の`Spark`コンストラクタのルートプレフィックスをベースにルートを作ります。
なので先程の`tasks`だと`tasks`というプレフィックスでルートをグループ化します。
そして基本は`post`通信でリクエストされるので`post`ルートを定義することになります。
そのため`Task`コントローラーを作る必要があります。

`php artisan make:controller TaskController`でコントローラーを作成してください。
`app/Http/Controllers/TaskController.php`を開き内容を以下のようにします。

```TaskController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TaskController extends Controller
{
    public function create (Request $request)
    {
        $name = $request->get('name');

        $task = new \App\Task;
        $task->order = \App\Task::all()->count();
        $task->name = $name;
        $task->is_finished = 0;
        $task->save();

        return \Lighter\Helper\Lesponse(true, [], 'Created the task.');
    }
}
```

`create`関数の`return \Lighter\Helper\Lesponse(true, [], 'Created the task.');`を見てください。
これの`Lesponse`関数は`Lighter response`の略で引数は`Lesponse(処理が成功したか, 返すデータ, メッセージ)`です。

再び`web.php`を開いてこの`TaskController`を対応させましょう。

`web.php`を以下のようにします。

```web.php
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::prefix('local_api')->group(function () {
    Route::prefix('tasks')->group(function () {
        Route::post('create', 'TaskController@create');
    });
});

Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');

```

次に`Tasks.create`関数で`tasks`テーブルにインサートするようにします。

`Tasks.js`を開いて以下のようにします。

```Tasks.js
import Spark from '../lighter/spark'

var Tasks = new Spark('tasks')

Tasks.create = async function (name)
{
    return await this.request('create', {name})
}

export default Tasks

```
`Tasks`オブジェクトに`create`関数を追加しました。
この`create`関数の中の`this.request('create', {name})`で通信を行います。
先程の`web.php`で定義した`tasks/create`に`name`を送っています。

では実際にこの`Tasks.create`関数を試してみます。

`resources/assets/js/app.js`を以下のようにします。

```app.js
import Vue from 'vue'

import Vuetify from 'vuetify'
import '../../../node_modules/vuetify/dist/vuetify.min.css'

import router from './router'
import store from './store'
import Lighter from './lighter'

import Tasks from './sparks/Tasks'
window.Tasks = Tasks //グローバルに使えるように

import '../sass/app.scss'

Vue.use(Vuetify)

Vue.component('app', require('@/layouts/App'))

const app = new Vue({
    router,
    store,
}).$mount('#app')
```

デベロッパーツールを開いてください。
コンソールで`Tasks.create('test')`を実行してください。

以下のように`tasks`テーブルに追加されているはずです。

![スクリーンショット 2018-07-11 14.50.48.png](https://qiita-image-store.s3.amazonaws.com/0/154299/23a0051c-5767-02df-40bc-902d01f99fcc.png)

これを利用して`CreateTask`ページに組み込んでみます。

## CreateTaskページの実装
`resources/assets/js/pages/CreateTask.vue`を以下のようにします。

```CreateTask.vue
<template>
    <div>
        <h1 class="display-3">CreateTask</h1>
        <v-card>
            <v-card-text>
                <v-text-field label="Name" v-model="name" :error-messages="nameErrors"></v-text-field>
                <v-btn color="primary" @click="create">Create</v-btn>
            </v-card-text>
        </v-card>
    </div>
</template>

<script>
import Tasks from '@/sparks/Tasks'

export default {
    name: 'TaskList',
    data () {
        return {
            name: '',
            nameErrors: []
        }
    },
    methods: {
        async create () {
            this.nameErrors = []

            if (this.name.length === 0) {
                this.nameErrors = ['Name is required!']
                return
            }

            await Tasks.create(this.name)

            this.$router.push('/')
        }
    }
}
</script>

<style>

</style>

```

<http://127.0.0.1:8000/create>を開き`Name`を適当に入力し`CREATE`ボタンを押してみてください。
タスクが追加されているはずです。
5こぐらい適当に作ってみてください。

## TaskListページの実装
TaskListには全タクスの情報が必要なので`Spark`から持ってこられるようにします。
まずコントローラーを編集します。

```TaskController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TaskController extends Controller
{
    public function create (Request $request)
    {
        $name = $request->get('name');

        $task = new \App\Task;
        $task->order = \App\Task::all()->count();
        $task->name = $name;
        $task->is_finished = 0;
        $task->save();

        return \Lighter\Helper\Lesponse(true, [], 'Created the task.');
    }

    public function all () //追加
    {
        return \Lighter\Helper\Lesponse(true, \App\Task::all(), 'Got all tasks.');
    }
}

```

次に`web.php`も以下のように変更します。

```web.php
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::prefix('local_api')->group(function () {
    Route::prefix('tasks')->group(function () {
        Route::post('create', 'TaskController@create');
        Route::post('all', 'TaskController@all'); //追加
    });
});

Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');

```

最後に`Spark`に実装します。
`resources/assets/js/sparks/Tasks.js`を以下のようにしてください。

```Tasks.js
import Spark from '../lighter/spark'
import alac from '../lighter/alac'

var Tasks = new Spark('tasks')

Tasks.create = async function (name)
{
    return await this.request('create', {name})
}

Tasks.all = async function (name)
{
    var spark = this
    var tasks = await this.request('all').then(alac.resultData)
    var models = []

    tasks.forEach(task => {
        models.push(spark.getModel(task))
    })

    return models
}

export default Tasks
```

`all`関数を追加してます。
`all`関数の中身を解説すると最初に`var spark = this`とあります。
これは後ほど配列を`forEach`で回す際に使うためです。
次に`var tasks = await this.request('all').then(alac.resultData)`でデータを取得します。
`alac`とは`axios`のラッパーで`Ajax Local API Client`の略です。`alac.resultData`関数でレスポンスを扱いやすく加工します。
次に`var models = []`配列を宣言しています。この中にレコードを使いやすくした`Model`が入ります。
次に`tasks`を`forEach`で回し各要素を`spark.getModel`関数に渡し、それを`models`に追加します。
最後に`models`配列を返して終了です。

これを使って`TaskList`を実装します。

`resources/assets/js/pages/TaskList.vue`を開いて以下のようにします。

```TaskList.vue
<template>
    <div>
        <h1 class="display-3">TaskList</h1>
        <v-list>
            <template v-for="task in tasks">
                <v-list-tile>
                    <v-list-tile-action>
                        <v-checkbox v-model="task.is_finished"></v-checkbox>
                    </v-list-tile-action>
                    <v-list-tile-title>
                        {{task.name}}
                    </v-list-tile-title>
                </v-list-tile>
                <v-divider></v-divider>
            </template>
            
        </v-list>
    </div>
</template>

<script>
import Tasks from '../sparks/Tasks'

export default {
    name: 'TaskList',
    data () {
        return {
            tasks: [],
        }
    },
    async mounted () {
        this.tasks = await Tasks.all()
    },
}
</script>

<style>

</style>

```

とりあえず表示すると
![スクリーンショット 2018-07-11 15.38.05.png](https://qiita-image-store.s3.amazonaws.com/0/154299/2993b522-c505-1da4-11ac-dd780ae31519.png)
こうなります。

しかしまだ保存機能がついていません。
`Spark`にはデフォルトで簡単にレコードを保存する機能がついています。
その機能を使用するために`TaskController`と`web.php`を編集します。

`app/Http/Controllers/TaskController.php`を開き以下のようにしてください。

```TaskController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TaskController extends Controller
{
    public function create (Request $request)
    {
        $name = $request->get('name');

        $task = new \App\Task;
        $task->order = \App\Task::all()->count();
        $task->name = $name;
        $task->is_finished = 0;
        $task->save();

        return \Lighter\Helper\Lesponse(true, [], 'Created the task.');
    }

    public function all ()
    {
        return \Lighter\Helper\Lesponse(true, \App\Task::all(), 'Got all tasks.');
    }

    public function update (Request $request)
    {
        $task = \App\Task::find($request->get('id'));
        $task->name = $request->get('name');
        $task->is_finished = $request->get('is_finished');
        $task->save();

        return \Lighter\Helper\Lesponse(true, [], 'Saved the task.');
    }
}

```

`routes/web.php`を以下のようにしてください。

```web.php
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::prefix('local_api')->group(function () {
    Route::prefix('tasks')->group(function () {
        Route::post('create', 'TaskController@create');
        Route::post('all', 'TaskController@all');
        Route::post('update', 'TaskController@update');
    });
});

Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');
```

こうすることでレコードを簡単に保存できるようになります。
コンソールを開いてください。

まず`var tasks = await Tasks.all()`を実行します。
次に`var task = tasks[0]`で最初のタスクを取り出します。
次に`task.name = 'task0'`を実行し、`name`を変更します。
最後に`task.save()`で保存完了です。

実際に確かめると
![スクリーンショット 2018-07-11 15.49.08.png](https://qiita-image-store.s3.amazonaws.com/0/154299/d182262d-06bf-86c8-b3bb-002855fa5eab.png)
`name`が`task0`に変わっています。
これを利用し保存機能をつけます。

`resources/assets/js/pages/TaskList.vue`を以下のようにしてください。

```TaskList.vue
<template>
    <div>
        <h1 class="display-3">TaskList</h1>
        <v-list>
            <template v-for="task in tasks">
                <v-list-tile>
                    <v-list-tile-action>
                        <v-checkbox v-model="task.is_finished" @input="save(task)"></v-checkbox>
                    </v-list-tile-action>
                    <v-list-tile-title>
                        {{task.name}}
                    </v-list-tile-title>
                </v-list-tile>
                <v-divider></v-divider>
            </template>
            
        </v-list>
    </div>
</template>

<script>
import Tasks from '../sparks/Tasks'

export default {
    name: 'TaskList',
    data () {
        return {
            tasks: [],
        }
    },
    async mounted () {
        this.tasks = await Tasks.all()
    },
    methods: {
        save (task) {
            task.save()
        }
    },
}
</script>

<style>

</style>
```

<http://127.0.0.1:8000/>を開き、適当なタスクのチェックボックスを入れて再読込してください。
![スクリーンショット 2018-07-11 15.53.53.png](https://qiita-image-store.s3.amazonaws.com/0/154299/454e6941-2a42-20db-a440-5831f5d1acd3.png)
ちゃんと反映されています。

## 編集機能の作成
`resources/assets/js/pages/TaskList.vue`を以下のようにしてください

```TaskList.vue
<template>
    <div>
        <h1 class="display-3">TaskList</h1>
        <v-list>
            <template v-for="task in tasks">
                <v-list-tile>
                    <v-list-tile-action>
                        <v-checkbox v-model="task.is_finished" @input="save(task)"></v-checkbox>
                    </v-list-tile-action>
                    <v-list-tile-title>
                        {{task.name}}
                    </v-list-tile-title>
                    <v-list-tile-action>
                        <v-btn color="success" @click="openEditDialog(task)">Edit</v-btn>
                    </v-list-tile-action>
                </v-list-tile>
                <v-divider></v-divider>
            </template>
            
        </v-list>

        <v-dialog v-model="editDialog" max-width="500">
            <v-card>
                <v-card-title><h1>Edit</h1></v-card-title>
                <v-card-text>
                    <v-text-field label="Name" v-model="editName" :error-messages="editNameErrors"></v-text-field>
                </v-card-text>
                <v-card-actions>
                    <v-btn flat @click="editDialog = false">Close</v-btn>
                    <v-spacer></v-spacer>
                    <v-btn color="primary" @click="applyEdit">Save</v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>
    </div>
</template>

<script>
import Tasks from '../sparks/Tasks'

export default {
    name: 'TaskList',
    data () {
        return {
            tasks: [],
            editDialog: false,
            editTask: null,
            editName: '',
            editNameErrors: [],
        }
    },
    async mounted () {
        this.tasks = await Tasks.all()
    },
    methods: {
        save (task) {
            task.save()
        },
        openEditDialog (task) {
            this.editTask = task
            this.editName = task.name
            this.editNameErrors = []
            this.editDialog = true
        },
        applyEdit () {
            this.editTask.name = this.editName
            this.editTask.save()
            this.editDialog = false
        }
    },
}
</script>

<style>

</style>
```
やっていることは先程ほぼ同じです。
`Edit`ボタンを押すとダイアログが開き、`Name`を入力し`Save`を押すと保存されます。

長くなりましたがこれで完成です。

# 今後の課題
今後の課題として
1. ドキュメントの作成
2. CLIツールの作成
などがあります。

# 最後に
今回作成したTODOアプリは`github`で公開しています。

<https://github.com/lighter-framework/lighter-qiita-todo-demo>

良ければアドバイスや意見をいただけると幸いです。