Semantic UIをFlaskで導入する

2020年12月20日

はじめに

PythonのWebアプリケーションフレームワークFlaskを使って、「消耗品買い物リスト」を作ってみるシリーズ第15回目です。

見た目を最低限整えるために、デザインフレームワークSemantic UIを導入します。

前回まで

インストール

公式サイトのGetting Startedを愚直にやります。

Getting Started | Semantic UI

どこにインストールするかですが、Flaskで静的コンテンツを配置するtbl/static直下に各種ディレクトリを置くことにします。

# node.jsのインストール
brew install node

# Gulpのインストール
npm install -g gulp


# ディレクトリ移動
$ cd tbl/static

# Semantic UIインストール
npm install semantic-ui --save
cd semantic/
gulp build

プロジェクトフォルダは現在地で良いか?どこにSemantic UIを配置するか?を聞かれますが、デフォルトで進めました。

インストールが完了すると、ビルドしたディレクトリ(ここでいうとstatic直下)に

  • node_modules
  • package-lock.json
  • semantic
  • semantic.json

のファイル/ディレクトリが作られます。
semanticディレクトリを見てみます。

$ ls -1 tbl/static/semantic/
dist
gulpfile.js
src
tasks

上記のdistディレクトリの中に実際に使うCSSやJSが格納されています。

セットアップ

インストールできたので、使えるか試します。

headでCSS/JSを読み込む

まずはSemantic UIのCSS/JSを読み込みむために、基本のテンプレートファイルを編集します。

  • tbl/templates/application.html
<head>
...
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='semantic/dist/semantic.min.css') }}">
<script src="{{ url_for('static', filename='semantic/dist/semantic.min.js') }}"></script>
...
</head>

※jQueryは前段ですでに読み込んでいるので割愛してます。

ヘッダーをつける

共通のヘッダーをつけます。

  • tbl/templates/application.html
...
<body>
  <div class="ui inverted large borderless menu column">
    <a class="header item" href="/">Listem</a>
  </div>
  <div class="ui container">
      {% block body %}
      {% endblock %}
  </div>
</body>
...

<div class="ui container">でコンテンツ部分をいい感じに囲んでおきます。

トップページを整える

最初のページを整えます。
ついでにキャッチコピーやサービス名も仮で入れておきます。
サービス名は仮で「Listem」にしました。

  • index.html
{% extends "application.html" %}
{% block body %}
<div class="ui container">
    <h1>Listem</h1>
    <p>買い忘れを無くしましょう。</p>
    <button class="ui button teal" onclick="location.href='page/create'">
        あなたの日用品買い物リストを作る
    </button>
</div>
{% endblock %}

<a href="...">でリンクを貼っていたものは、ボタンに差し替えています。

<button class="ui button teal" onclick="location.href='page/create'">

こんな感じになります。

アイテム一覧ページを整える

メインのアイテム一覧ページを差し替えていきます。

Flashメッセージ

  • user_page.html
...
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class='flash ui success message'>
    <ul class=flashes>
    {% for message in messages %}
    <li>{{ message }}</li>
    {% endfor %}
    </ul>
</div>
{% endif %}
{% endwith %}
...

<div class='flash ui success message'>で囲むようにしました。

アイテムリスト

  • user_page.html
...
<div class="ui middle aligned divided list">
    {% if page.items.count() > 0 %}
        {% for p in page.items %}
        <div class="item">
            <div class="right floated content">
                <div class="ui icon button">
                    <a href="{{ url_for('edit_item', item_id=p.id, uuid=page.uuid) }}"><i class="edit icon"></i></a>
                </div>
                <div class="ui icon button">
                    <a href="{{ url_for('delete_item', item_id=p.id, uuid=page.uuid) }}"><i class="trash icon"></i></a>
                </div>
            </div>
            <div class="content">
                <div class="header">{{ p.name}} </div>
                Last update: {{ p.updated_at }},
                Cycle days: {{ p.purchase_cycle_days }},
                Stock rate: {{ p.stock_rate }},
                Status: {{ p.status }}
            </div>
        </div>
        {% endfor %}
    {% else %}
        <p>No item set yet.</p>
    {% endif %}
    <div class="item">
        <form name="input" class="ui form" id="new_item" action="item/create" accept-charset="UTF-8" method="post">
            {% with submit_word = 'Add Item' %}
            {% include "item_form.html" %}
            {% endwith %}
        </form>
    </div>
</div>
...
  • <div class="ui middle aligned divided list">で、子要素を線で区分けされたリストにすることができます。
  • 子要素は<div class="item">...</div>で作ります。
  • <div class="right floated content">...</div>には編集/削除ボタンを入れています。
    • <div class="ui icon button">...<i class="XXX icon"></i></div>と書くと、Semantic UIが用意しているアイコン画像を利用できます。
  • <div class="content">...</div>にはアイテム情報を記載しています。
  • {% endif %}以降は、新規アイテム追加用のフォームになっています。
  • <form ... class="ui form">とすることでSemantic UIの用意したFormがつかえます。

アイテムフォーム部品の変更

user_page.htmlで呼び出しているフォームの部品も差し替えます。

  • item_form.html
<div class="ui form">
    <div class="fields">
        <div class="inline field">
        <label>品目名: </label>
        <input type="text" class="validate[required]" name="name" id="name"
            {% if submit_word =='Edit item' %} value="{{ item.name }}"{% endif %}/>
        </div>

        <div class="inline field">
        <label>購入サイクル(日数): </label>
        <input type="number" class="validate[custom[number]]" name="purchase_cycle_days" id="purchase_cycle_days" min="1" max="90"
            {% if submit_word =='Edit item' %} value="{{ item.purchase_cycle_days }}"{% endif %}/>
        </div>

        <div class="inline field">
        <label>残量ステータス(0.0〜1.0): </label>
        <input type="number" class="validate[custom[number]]" name="stock_rate" id="stock_rate" min="0.0" max="1.0" step="0.1"
            {% if submit_word =='Edit item' %} value="{{ item.stock_rate }}"{% endif %}/>
        </div>

        <div class="right floated">
        {% if submit_word == 'Edit item' %}
            <input type="hidden" name="item" value="{{ item.id }}" />
            <input type="hidden" name="user_page_id" value="{{ item.user_page_id }}" />
        {% else %}
            <input type="hidden" name="user_page_id" value="{{ page.id }}" />
        {% endif %}
        <button class="ui button" type="submit">{{ submit_word }}</button>
        </div>
    </div>
</div>
  • <div class="ui form">で囲んでいること
  • <div class="inline field">にして一行にラベルと入力フィールドが収まるようにしていること

あたりが主な変更点になります。

アイテム編集ページも整える

あまり変えるところは無いですが、アイテム編集ページもuser_page.htmlの書き方に合わせて変更します。

  • item_edit.html
{% extends "application.html" %}
{% block body %}
<div>
    <h1>{{ title }} : {{ item.name }}</h1>
    <div>
        <form name="input" class="ui" id="new_item" action="/item/edit/{{item.id}}/{{uuid}}" accept-charset="UTF-8" method="post">
            {% with submit_word = 'Edit item' %}
            {% include 'item_form.html' %}
            {% endwith %}
        </form>
    </div>
</div>
{% endblock %}

見た目確認

トップページ

新規ページ作成

アイテムいくつか追加

アイテム編集ページ

終わりに

Semantic UIを入れることで、少しだけマシな見た目になりました。

見た目をいじるのはめちゃくちゃ楽しいのですが、今いじると泥沼にハマるので、このあたりでとどめておきます。
次回以降はまた機能に戻り、必要な機能を作り込んでいきます。

参考