スクリプトのお勉強 技術

Nuxt.js – CRUDアプリケーションのフォーム/一覧を作成する

投稿日:

前回で作ったAPIのフロントエンドアプリケーションを作ろうと思います。

どういうアプリ?

サンプルとして作ったAPIが住所録的だったので、住所録を作りました。

以下の機能があります。

  • 登録(確認付き)
  • 更新(確認付き)
  • 削除(確認ダイアログ付き)
  • 一覧表示

特徴

取り立てて特徴はありませんが、以下を追加してみました。

  • ほぼ全部TypeScript
    Nuxt.jsはあまりTypeScriptにやさしくないので使ったことがなかったのですが、
    今回はTypeScriptを採用してみました。
  • vee-validateによる親子コンポーネント間のバリデーション
     子コンポーネントのバリデーション結果を親コンポーネントで判定してみました。
  • デコレータ(vuex-module-decorators)を使用しない
     なんとなくデコレータが好きじゃないので、使わずに書いてみました。 

環境

  • nodejs: v12.16.3
  • yarn: 1.12.3
  • nuxt.js: 2.14.7

準備

以下で作り始めました。

$ npx create-nuxt-app

ソース(ディレクトリ)構成

ソースはここにあります。

以下が結果のソースです。

|-- components
|   |-- container
|   `-- present
|       |-- addr_form.vue         :登録フォーム
|       |-- addr_form_readonly.vue    :登録フォーム(確認用)
|       |-- addr_id_form.vue          :更新フォーム
|       |-- addr_id_readonly_form.vue :更新フォーム(確認用)
|       `-- confirm_dialog.vue        :削除確認用ダイヤログ
|-- layouts
|   |-- default.vue
|-- lib
|   |-- addr.ts                      :addrの型を設定
|   `-- api.ts             :axios API呼び出し用
|-- nuxt.config.js
|-- package.json
|-- pages
|   |-- addr_add.vue         :登録画面
|   |-- addr_confirm.vue       :登録画面(確認)
|   |-- addr_edit_confirm.vue    :更新画面(確認)
|   |-- addr_edit_save.vue      :更新画面(保存)
|   |-- addr_save.vue               :登録画面(保存)
|   |-- addrs.vue           :一覧画面
|   |-- editaddr
|   |   `-- _id.vue                 :更新画面
|   `-- index.vue  
|-- plugins
|   |-- axios.js           :$axios用プラグイン
|   `-- vee-validate.js             :フォームバリデーション設定
|-- store
|   `-- index.ts           :addrs(APIで保持している住所の配列)       
|-- tsconfig.json
|-- types
|   |-- index.d.ts          :TypeScriptの型指定
|   `-- vue-shim.ts
`-- yarn.lock

コンポーネントの種類

コンポーネントを以下のように分類してみました。よくある分類だと思います。

  • components/container <– データをもつコンポーネント。
  • components/present <– prop経由でしか持たない。callbackは親コンポーネント側を使用する。

と言っても、今回はcontainerはありませんでした。

起動方法

今回は、FastAPIとUI側で別のURLにしてみました。

FastAPI側の起動は以下の通りです。

$ pipenv run uvicorn app.run:app --reload --host 0.0.0.0 --port 18001

UI側の起動は以下の通りです。192.168.132.128:18000はテスト環境での値なので、
適宜変更してください。

$ cd mvc
$ yarn install
$ yarn run dev
? Listening on: http://192.168.132.128:18000/
No issues found.

CORS回避(API側)

CORSを回避するため、API側でoriginが違う場合の対処をしてみました。

FastAPI側で以下の対処を追加すれば、CORSを回避できます。
originsはUI側からどのようにアクセスしに来るかによります。

+from fastapi.middleware.cors import CORSMiddleware
 import os
 from starlette.requests import Request
 import sys
@@ -12,11 +13,24 @@ sys.path.append(ROOT_DIR)
 from app.urls import router as app_router
 from app.settings.settings import Settings

+origins = [
+    "http://192.168.132.128:18000",
+    "http://192.168.0.12:18000",
+]

 settings = Settings()

 app = FastAPI(openapi_url='/openapi.json')
 app.add_middleware(DBSessionMiddleware, db_url=settings.DATABASE_URI)

+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=origins,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+

vee-validateによる親子コンポーネント間のバリデーション

思ったよりは簡単で、子側は以下のように、v-slotとrulesを設定し

<validation-provider v-slot="{ errors }" rules="required|ipaddr" name="IPアドレス" >
<v-text-field
    v-model="addrObj.ipaddr"
    label="IPアドレス"
    :error-messages="errors"
    required
/>
</validation-provider>

親側には以下を設定し、submitのタイミングでvalidationを呼び出せばいいだけでした。

<validation-observer ref="validationObserver" tag="div">
  <AddrForm :addrObj=addrObj />
</validation-observer>

methods: {
  async submit(addrObj) {
    const isValid = await this.$refs.validationObserver.validate();
    if (!isValid) return false;
    this.$router.push({ name: 'addr_confirm', params: { addrObj: addrObj }});
  }
},

開発する際にはまったこと

むしろはまったことしかありません。。多すぎて覚えてない。。

前半は、そもそも何も動かなくて、後半はnuxt.config.jsやtsconfig.jsonに設定をする必要があったことが多い気がします。

まだ分からない点

いまだに実装上分かってないことがあります。

FastAPIで戻り値がおかしい

以下のようにDBのupdate後の値をAPIで戻すべくModelを返却しているのですが、なぜか辞書にしないとUI側で値が取れなかったりします。ちゃんと理由を調べてない。。

def asdict(self):
    result = OrderedDict()
    for key in self.__mapper__.c.keys():
        if getattr(self, key) is not None:
            result[key] = str(getattr(self, key))
        else:
            result[key] = getattr(self, key)
    return result


def update_addr_query(db: Session, addr: schemas.AddrUpdateEntity):
    db_addr = db.query(models.Addr).filter(models.Addr.id==addr.id).first()

    db_addr.name = addr.name
    db_addr.addr = addr.addr
    db_addr.ipaddr = addr.ipaddr
    db_addr.update_time = datetime.datetime.now()

    db.commit()
    # 辞書に変更しないと、modelsがそのまま反映されてしまうため
    asdict(db_addr)
    return db_addr

/addrsをリロード(Ctrl+R)すると画面が崩れる

リロードが終わると正常なんですが、、よく分かってません。

終わりに

思った通り苦労しました。作ったつもりでいきなり動かない。動かないけど、どこをどう調べていいかよく分からない。

なのでしょうがなく、これのように小さく作って、段々と大きくしていくことにしてます。。

参考

https://qiita.com/shindex/items/a90217b9e4c03c5b5215

  • Nuxt.jsのTypeScript環境

https://qiita.com/ikedaHi/items/1001594a386815c0ce85

  • ボダンでの遷移、コンポーネント間通信、syncのやりかた。

https://qiita.com/after666/items/7e94d61fed89406ae59a

  • actionsの引数について

https://qiita.com/tshcstrm/items/aeb9cc7da014fd2b8304

  • TypeScriptでのpropsの書き方。

https://se-tomo.com/2018/11/03/vue-js-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E9%96%93%E3%81%AE%E9%80%9A%E4%BF%A1/

  • 親子間通信
  • emit
  • 親子間のイベント伝達

https://note.com/aliz/n/ne1d2fc4fb1ef

  • nuxt.jsでのvue-router

https://typescript.nuxtjs.org/cookbook/store.html#class-based

  • classベースの書き方(今回はVanilla的に記述)

-スクリプトのお勉強, 技術

執筆者:

関連記事

Markdown to HTML(grip)

1.はじめに 最近の文書はほとんどMarkdownで書くのですが、それをHTMLに変換する方法を調べました。 ただし、私がMarkdownと思っている文法は「github-flavored-markd …

SPAMチェック for OCN の開発

前回、Thunderbirdプラグインの概要を書いたので、今回は開発したプラグインについて書きます。 SPAMチェック for OCNとは 以下の機能を持ったThunderbirdのアドオン(プラグイ …

Python3 – VCR.py でネットワーク系テストを簡単に作成する

1. 始めに python3で実装すると、モックテストをしたくなります。モックを使って、比較的簡単にテストできるからです。 問題はネットワーク系テスト モックテストで問題になるのは、外部に依存するテス …

PythonでAESを使用して暗号/復号する

1.つづき Pythonで、PKCS#12の公開鍵で暗号、秘密鍵で復号するプログラムの続きです。 今回はAESで暗号化/復号を行い、通信しながらRSA暗号/復号,AES暗号/復号を組み合わせたいと思い …

Pipenvでライブラリ(*.whl)をキャッシュする方法

仕事でWebアプリケーションを作成した場合、ガンガンとライブラリのバージョンを上げたりはしない場合があります。 そして、長期間放置されるタイプのWebアプリケーションの場合、使用しているライブラリが、 …