2020年12月12日

プログラミング

Blitz.jsとTailwind CSSでメモ帳アプリの作成【第2弾】

目次

  1. はじめに
  2. メモのカテゴリーを作成
  3. seedファイルの作成
  4. Tailwind CSSとは
  5. Tailwind CSSの導入
  6. サイドバーの作成
  7. まとめ

はじめに

新しい技術にチャレンジし続けるpalanのアドベントカレンダーDay12です!

昨日は「『えのすい七五三デジタルフォトフレーム』のデザイン作成フローについて」を書きました!

『えのすい七五三デジタルフォトフレーム』のデザイン作成フローについて

本記事では前回に引き続きBlitzjsとTailwind CSSでメモ帳アプリを作りたいと思います。

第2弾でやること

  • メモのカテゴリーを作成
  • seedファイルの作成
  • サイドバーの作成

また,今回作成したコードの全体像はこちらになります。

ogp.png (65.2 kB)

メモのカテゴリーを作成

カテゴリーテーブルの作成

・category has_many memos
・category belongs_to user

という関係になるように,categoryテーブルを作成します。nameをカラムとして持つようにします。memoテーブルを作成したときと同様にblitz generateコマンドで作成していきます。

$ blitz generate all category name:string memos:Memo\[] belongsTo:user

すると,schema.prismaに

model Category {
  id        Int      @default(autoincrement()) @id
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  name      String   
  memos     Memo[]   
  user      User     @relation(fields: [userId], references: [id])
  userId    Int      
}

が追加されるので,

$ blitz db migrate

でDBに反映させます。

補足:blitz generateについて

ここで,blitz generateについて詳しく見てみましょう。https://blitzjs.com/docs/cli-generate にある通りblitz generate all以外にもresourcemodelなどあり,どこまで自動生成するか決められます。

image.png (69.2 kB)

また,has manybelongs toについてもドキュメントに書いてあり,

$ blitz g model project tasks:Task[]
$ blitz g model task belongsTo:project

という例が載っています。blitz gblitz generateの省略形です。
今回はQueriesやPagesも作成したかったため,blitz g modelの代わりにblitz g allを使いました。

メモテーブルにカテゴリーとの関連を追加

既にメモのPageやQueriesは存在しているので,今回はblitz g allではなくblitz g modelを使ってModelの変更のみ行います。

$ blitz g model memo belongsTo:category

schema.prismaのMemoにcategoryIdとcategoryが追加されました。

model Memo {
  id        Int      @default(autoincrement()) @id
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  title     String   
  body      String   
  user      User     @relation(fields: [userId], references: [id])
  userId    Int      
}

model Memo {
  id        Int      @default(autoincrement()) @id
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  title     String   
  body      String   
  user      User     @relation(fields: [userId], references: [id])
  userId    Int      
  category   Category @relation(fields: [categoryId], references: [id])
  categoryId Int      
}

$ blitz db migrateを忘れずに行ってください。

注意!!

既にメモテーブルにデータが入っている場合,migrationする際にcategoryIdが入っていない状態になるのでエラーが発生してしまいます。その場合, `$ blitz db reset` を実行してDBをリセットしてください。

CRUD機能

memoテーブルのときと一緒なので割愛させていただきます。 memoテーブルのCRUDは前回の記事 の「CRUD機能の実装」の章を参考にしてください。

以上でカテゴリーの完成です!🎉

カテゴリー作成の全体像はこちらのPRになります。

seedファイルの作成

スタイルを当てる前にデータを入れておきたいので,seedを作成しておきましょう。

ドキュメントに書いてある通り,db/seed.tsが最初から用意されているのでこちらに追記していきます。

userの作成

まずuserから作成していきます。既に存在するapp/auth/mutations/signup.tsのコードを参考に書いていきます。

  const hashedPassword = await hashPassword("password!!")
  const email = "hoge@example.com".toLowerCase()
  const userId = (
      await db.user.create({
        data: { email, hashedPassword, role: "user" },
        select: { id: true, name: true, email: true, role: true },
      })
    ).id
categoryとmemoの作成

先程作成したuserのidを使います。 以下のように親と子のデータを同時に作成できます。

  await db.category.create({
    data: {
      name: "カテゴリー1",
      memos: {
        create: [
          {
            title: "メモ1",
            body:
              "メモの1つ目です。",
            user: { connect: { id: userId } },
          },
          {
            title: "メモ2",
            body:
              "メモの2つ目です。",
            user: { connect: { id: userId } },
          },
        ],
      },
      user: { connect: { id: userId } },
    },
  })
DBに反映

$ blitz db seedを実行したらDBに反映完了です🎉

変更されたコード全体はこちらのPRを参照してください。

Tailwind CSSとは

Tailwind CSSについてはアドベントカレンダーDay6の記事で分かりやすく説明しています。「Tailwind CSSとは」の箇所をぜひ読んでみてください!

Tailwind BuilderでReactのコードを出力しながらTailwind CSSを学んでみる

Tailwind CSSの導入

$ blitz install tailwind

これだけで導入完了です!🎉

Recipeについて

Blitz.jsでは,Recipeというものがあります。Recipeを使うことでサードパーティー製のコードをたったひとつのコマンドで導入することができます。 GatsbyのRecipeにインスパイアされたみたいですね。

https://github.com/blitz-js/blitz/tree/canary/recipes にあるものは簡単に導入できます。Tailwind CSSの他にも
chakra
・material-ui
・emotion
などがあります。chakraも最近話題になっていますね。

補足1:recipeができたのは2020年9月のこのPRで,それまでは手動で色々設定する必要がありました。
補足2:Tailwind CSSのv2がリリースされたのは2020年11月9日ですが,11月下旬にRecipeにも反映されました!(PR:https://github.com/blitz-js/blitz/pull/1486) 対応がとても早いですね✨

サイドバーの作成

サンプル

sidebarの例は公式にも載っていますが有料なので,今回はtailwindcomponentsというサイトの
https://tailwindcomponents.com/component/app-sidebar
を参考にしました。

デフォルトのスタイルの削除

まず,app/pages/index.tsxに書いてある

<style jsx global>{`
...
`}</style>

はデフォルトのstyleなので全て消しておきましょう。

asideとmainの構成をつくる

app/layouts/Layout.tsxが元々用意されているので,ここに書いてみましょう。

const Layout = ({ title, children }: LayoutProps) => {
  return (
    <>
      <Head>
        <title>{title || "memoApp"}</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <div className="flex bg-gray-100 min-h-screen w-screen">
        <aside className="bg-white w-60 h-screen sticky top-0">
          <div className="flex items-center justify-center mt-10">
            <a href="/">Application</a>
          </div>
          <nav className="mt-10">
            <a
              className="flex items-center py-2 px-8 bg-gray-200 text-gray-700 border-r-4 border-gray-700"
              href="#"
            >
              ああああ
            </a>
            <a
              className="flex items-center py-2 px-8 text-gray-600 border-r-4 border-white hover:bg-gray-100 hover:text-gray-700 hover:border-gray-700"
              href="#"
            >
              いいいい
            </a>
          </nav>
        </aside>

        <main className="w-full">{children}</main>
      </div>
    </>
  )
}

すると以下のようになります。

image.png (313.9 kB)

一部解説すると,
bg-gray-100のようにbg-から始まるところはbackground colorです。
mtはmargin topの略です。mbmrでbottomやrightにつけられる他,myで上下,mxで左右につけることができます。また,mだけだと4方向につけられます。
pyはpaddingで,marginと同様です。
border-rはborder-rightです。
・min-h-screenでmin-height: 100vhになります。

Sidebarコンポーネントの作成

app/layoutsのディレクトリにSidebar.tsxを作成します。
カテゴリー一覧を取得して表示するところまで実装すると以下のようになります。

import { useQuery, useRouter } from "blitz"
import getCategories from "app/categories/queries/getCategories"
import { useCurrentUser } from "app/hooks/useCurrentUser"
import { Category } from "@prisma/client"

const NavItem = ({ category, isActive }: { category: Category; isActive: boolean }) => {
  const className = isActive
    ? "bg-gray-200 text-gray-700 border-r-4 border-gray-700"
    : "text-gray-600 border-r-4 border-white hover:bg-gray-100 hover:text-gray-700 hover:border-gray-700"

  return (
    <a className={`flex items-center py-2 px-8 ${className}`} href={`/categories/${category.id}`}>
      {category.name}
    </a>
  )
}

const Sidebar = () => {
  const currentUser = useCurrentUser()
  const router = useRouter()
  const currentCategoryId = router.params.categoryId
  const [{ categories }] = useQuery(getCategories, {
    orderBy: { name: "asc" },
    where: { userId: currentUser?.id },
  })

  return (
    <aside className="bg-white w-60 h-screen sticky top-0">
      <div className="flex items-center justify-center mt-10">
        <a href="/">Application</a>
      </div>
      <nav className="mt-10">
        {categories.map((category) => (
          <NavItem
            category={category}
            isActive={Number(currentCategoryId) == category.id}
            key={category.id}
          />
        ))}
      </nav>
    </aside>
  )
}

export default Sidebar

また,app/layouts/Layout.tsxを以下のように変更します。

const Layout = ({ title, children }: LayoutProps) => {
  return (
    <>
...

      <div className="flex bg-gray-100 min-h-screen w-screen">
        <Suspense fallback="Loading...">
          <Sidebar />
        </Suspense>
        <main className="w-full">{children}</main>
      </div>
    </>
  )
}

これで完成です🎉

image.png (292.9 kB)

サイドバーをタップするとカテゴリーのページに飛ぶことができます。
image.png (71.1 kB)

POINT1

orderBy: { name: “asc” }とすることで,カテゴリーを名前順にならべています。

POINT2

router.params.categoryIdで/cartegories/[categoryId]のcategoryIdの部分を取得しています。その値を使ってsidebarの各カテゴリーがアクティブにするかどうかを判断しています。

変更されたコード全体はこちらのPRを参照してください。

まとめ

Tailwind CSSを使うことで,CSSを書かずに素敵なスタイルを当てることができました。

また,Reactの公式では

React doesn’t have opinions on how you put files into folders

と言及しているように「ディレクトリ構成をこうすべき」という方針は無いため,プロジェクト間の差異が大きく,ベストプラクティスがないというのがデメリットでした。一方,Blitz.jsでは元からLayoutのファイルがあったりseedのファイルが用意されていることで,「どのディレクトリに置こう」と悩むことなく進めることができ,誰が書いても同じようなディレクトリ構成にすることができるのではないでしょうか。

第3弾では引き続き他の画面にもスタイルを当てていきたいと思います。

Reactのお仕事に関するご相談

Bageleeの運営会社、palanではReactに関するお仕事のご相談を無料で承っております。
zoomなどのオンラインミーティング、お電話、貴社への訪問、いずれも可能です。
ぜひお気軽にご相談ください。

無料相談フォームへ

1

0

AUTHOR

まりな

まりな

アプリでもっと便利に!気になる記事をチェック!

記事のお気に入り登録やランキングが表示される昨日に対応!毎日の情報収集や調べ物にもっと身近なメディアになりました。

簡単に自分で作れるWebAR

「palanAR」はオンラインで簡単に作れるWebAR作成ツールです。WebARとはアプリを使用せずに、Webサイト上でARを体験できる新しい技術です。

palanARへ
palanar

palanはWebARの開発を
行っています

弊社では企画からサービスの公開終了まで一緒に関わらせていただきます。 企画からシステム開発、3DCG、デザインまで一貫して承ります。

webar_waterpark

palanでは一緒に働く仲間を募集しています

正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。

話を聞いてみたい

運営メンバー

eishis

Eishi Saito 総務

SIerやスタートアップ、フリーランスを経て2016年11月にpalan(旧eishis)を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。

sasakki デザイナー

アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。

しまだ

しまだ デザイナー

WebAR/VRのデザインと3DCG制作がメインです。 肩書きは「アニメ案件に関わりたいデザイナー」。

Miu マーケター

ドイツでWEBマーケティングしています。

しんのき エンジニア

主に React Native を使ったアプリ開発と AWS や Firebase を使ったサーバーレスアーキテクチャを担当しています。元々はインフラとかPHPをやっていました。

yamakawa

やまかわたかし デザイナー

フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReactやReact Nativeをよく触っています。

furuya エンジニア

サーバーサイド、フロントエンド、Unityと色々手を出してる雑食系エンジニア。ReactNativeが最近のマイブーム。

Sayaka Osanai デザイナー

Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。

はらた

はらた エンジニア

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

うえまつゆい エンジニア

サーバーサイドエンジニアからフロントエンドエンジニアになりました。主にReact Nativeでのアプリ開発をしています。

kobori

こぼり ともろう エンジニア

サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。

sasai

ささい エンジニア

フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。

damien

Damien

WebAR/VRを中心に企画・マークアップ・開発をやっています。森に住んでいます。

デザイナーゲスト

ゲスト デザイナー

かっきー

かっきー

まりな

まりな

suzuki

suzuki

taro

taro

xR界隈のビズをやっています。新しいガジェットとか使うのが好きです。あとお寿司は玉子のお寿司が好きです。

miyagi

ogawa

ogawa

雑食デザイナー。UI/UXデザインやコーディング、時々フロントエンドやってます。最近はARも。

いわもと

いわもと

kobari

taishi kobari

フロントエンドの開発を主に担当してます。Blitz.js好きです。

CONTACT PAGE TOP