おもこん

おもこんは「思いつくままにコンピュターの話し」の省略形です

徒然Ruby(28)Rails7とBootstrap5

一般に、HTMLは文書の構造を表し、CSSはその体裁(見栄え)を表します。 Railsは最終的にCSSを含むHTML文書を出力するので、この2つについての理解が必須です。 この記事ではとくにCSSの人気ライブラリであるBootstrapを紹介します。 BootstrapはJavascriptも含んでいます。

インストール

RailsとBootstrapの両方とも開発スピードが速いです。 そのため両者を組み合わせてインストールするのは、なかなか難しいものがあります。

  • インターネット情報は古いものが多い
  • 最新のバージョンの組み合わせに対する情報が少ない

今回ここで紹介するのはRails7とBootstrap5の組み合わせで「rails new」コマンドに「-c bootstrap」または「--css=bootstrap」オプションをつける方法です。 自分にとっても新しい方法なので、この記事が正しいと保証することができません。 ひとつの体験談として、また数ある情報のひとつとしてお考えいただきたいと思います。 また、この記事に基づいて行ったことから発生する問題に対して何ら保証するものではありませんので、自己責任でお願いします。

(補足)古いバージョンのrailsではgemを使っていました。 たとえば、bootstrapbootstrap-sassなどです。 Rails7でもそれらを使うことは可能だと思います。 ただし、bootstrap-sassはBootstrap3にしか対応していません。 rails7では、newコマンドでBootstrap5の導入が簡単にできるようになりました。 ここで用いた方法以外にCDNを使う方法も可能だと思います(検証不十分)。 CDNはBootstrapで推奨されている方法で、詳しくはBootstrapのサイトを見てください。

私のPC環境は「Ubuntu 22.04」です。

rails new word_book_rails -c bootstrapを行うと、エラーが出ました。

error @hotwired/turbo@7.2.2: The engine "node" is incompatible with this module. Expected version ">= 14". Got "12.22.9"
error Found incompatible module.

Ubuntu22.04の現在のnodejsのバージョンが12.22.9なので、それが古いというエラーだと思われます。 nodejsの新しいバージョンをインストールする情報がnodesource/distributionsにあります。 エラーメッセージではバージョン14以上が必要となっていますが、最新が18なので、それをインストールしてみます。 nodesource/distributionsのREADME.mdの情報によると、次のコマンドを実行すればよいとのことです。

curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - &&\
sudo apt-get install -y nodejs

実行したところなぜかエラーになり、その後sudo apt autoremovesudo apt remove nodejssudo apt install nodejsしたところ最新版がインストールできました。 エラーが気になりますが、一応次のようになっています。

$ node --version
v18.11.0

word_book_railsフォルダを削除して、再度newコマンドで作成します。

$ rm -rf word_book_rails
$ rails new word_book_rails -c bootstrap

たくさんのメッセージが現れ、数多くの作業が行われていることが感じられます。 途中で

npm WARN set-script set-script is deprecated, use `npm pkg set scripts.scriptname="cmd" instead.

という警告が出ますが、エラーではないので問題ないでしょう。 また、次のようなメッセージも出ます。

File unchanged! The supplied flag value not found!  app/assets/config/manifest.js

これがエラーなのか、警告なのか、あるいは問題のない状況なのかは良く分かりませんでした。 しかし、フォルダ内を見渡したところ、Bootstrapはインストールできているので、このまま次の作業に入りたいと思います。

Bootstrapとは?

BootstrapはCSSJavascriptのライブラリです。 これを使うことによって、ウェブの見栄えを美しくしたり、プルダウンメニューのような動的画面を簡単に作ることができます。 これをBootstrap無しで一から作るとしたら、膨大な時間がかかります。 Bootstrapは本当に神様のようなライブラリです(しかも無料)。 また、CSSJavascriptをよく知らなくても使えるので、学習コストを大幅に下げることができます。

具体的な例はBootstrapのウェブサイトを参照してください。 例えば「Components」の「Buttons」を見ると、サンプルとコードがあります。

Bootstrap=>ドキュメント=>Components=>Buttons

<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-secondary">Secondary</button>
<button type="button" class="btn btn-success">Success</button>
<button type="button" class="btn btn-danger">Danger</button>
<button type="button" class="btn btn-warning">Warning</button>
<button type="button" class="btn btn-info">Info</button>
<button type="button" class="btn btn-light">Light</button>
<button type="button" class="btn btn-dark">Dark</button>

<button type="button" class="btn btn-link">Link</button>

サンプルのような色、形のボタンを作るには、対応するコードを書けば(コピペすれば)良いだけです。

また、「Layout」の「Containers」はレスポンシブデザイン(ディスプレイ画面の大きさに合わせて描画領域を自動的に変更する)のコンテナ(箱)です。 bodyタグの内側を<div class="container"></div>で囲めば、その内容を画面サイズに合うようにサイズ調整してくれます。

この他にも有用な機能が数多くあるので、良く見てください。

Bootstrapの使用例

今回はBootstrapのコンテナ、コラムとナビゲーションバーを使おうと思います。

作成する単語帳のウェブサイトを「Wordbookサイト」または単に「Wordbook」と呼ぶことにします。

コンテナ

Wordbookのすべてのビュー(画面)はレスポンシブデザインになるように、コンテナの中にいれます。 すべてのビューはapplication.html.erbの中に埋め込まれるので、このファイルにコンテナを設定します。

app/views/layouts/application.html.erbをエディタで開き、ボディタグの中身を次のように変更します。

<body>
  <div class="container">
    <%= yield %>
  </div>
</body>

それぞれのビューはyieldのところに埋め込まれるので、すべてのビューがclass="container"のdivタグに囲まれ、レスポンシブになります。 この効果は後ほど見ることにします。

パーシャルとナビゲーションバー

Wordbookのビューの多くはナビゲーションバーを持ちます。 ナビゲーションバーには、append、change、delete、listなどのメニュー(実際にはリンク)を設定します。 すべてのビューではないのでapplication.html.erbに書くことはできないのですが、多くのビューで使うものなのでパーシャルに記述します。 パーシャルはビューから呼ばれ、そこに埋め込まれるファイルです。

<%= render "abc" =>

とビューの中で書かれた部分はapp/views/_abc.html.erbの内容で置きかえられます。 ファイル名の先頭にはアンダースコア(_)がつくことに注意してください。

Bootstrapのウェブの「Components=>Navbar」に例があります。 そのコードをコピペしてから改変します。 以下にパーシャルapp/views/_navbar.html.erbの内容を示します。

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <div class="container-fluid">
    <a class="navbar-brand" href="#">Navbar</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
        <li class="nav-item">
          <a class="nav-link active" aria-current="page" href="/">Home</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="/words/append">Append</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="/words/change">Change</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="/words/delete">Delete</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="/words/search">Search</a>
        </li>
    </div>
  </div>
</nav>

10行目から24行目までが改変部分で、メニュー名とリンク先以外はすべてコピペです。 順序なしリスト(<ul>タグで囲まれた部分)の各項目は次の通りです。

  • Home=>初期画面に戻る
  • Append=>単語を追加する画面に遷移する
  • Change=>単語を変更する画面に遷移する
  • Delete=>単語を削除する画面に遷移する
  • Search=>単語を検索しマッチした単語のリスティング画面に遷移する
ビュー

ビューでは「単語帳」という見出しと使い方の順序なしリストを表示することにします。 リストの各項目が短い文字列なので、コンテナの幅のままだとPC画面では横長になりすぎます。 (例えば私のノートPCは幅が1920ピクセルなので、コンテナの幅は1320ピクセルに設定されます)。 他方、スマホのような小さい画面ではコンテナは全画面を使用し、それが最適な表示になります。

そこで、画面サイズに応じた表示幅を設定しなければなちません。 Bootstrapでは画面幅を次のように分けています。

ブレークポイント クラスの接中辞 画面幅の範囲 コンテナの幅
X-Small なし <576px 100%
Small sm ≥576px 540px
Medium md ≥768px 720px
Large lg ≥992px 960px
Extra large xl ≥1200px 1140px
Extra extra large xxl ≥1400px 1320px

例えば、私のPC画面は幅1920pxでxxlになりますから、コンテナの幅は1320pxです。

BootstrapではX-Smallからxxlまでのどの画面でもコンテナの幅を12等分してカラム(列)を作ります。 3カラム分の幅をとりたいときはクラスにcol-3を指定すればよいのです。 この幅は3/12=1/4なので、コンテナの幅の4分の1になります。 (詳しくはBootstrapのカラムのStandalone Column Classesを参照してください)。

また、例えば、画面サイズがMediumのときには6カラム分にしたければcol-md-6とクラスに指定します。 次の例を見てください。

<div class="col-sm-10 col-lg-8 col-xl-6 mx-auto">
... ... ...
... ... ...
</div>
  • sm、lg、xlが使われている。
    • xlは1200px以上なので、xxlも含めて6カラムになる=>xlが1140/12*6=570、xxlが1320/12*6=660
    • lgは8カラム=>960/12*8=640
    • smは576px以上なので、mdも含めて10カラムになる=>smが540/12*10=450、mdが720/12*10=600
    • 上記で指定されていないX-Small(576px未満)は「コンテナのサイズ(12カラム)=画面のサイズ」になる
  • mx-autoは、(m)マージンの(x)左右を(auto)自動にする。 このときコンテンツがセンタリングされる状況になる。 (W3C CSSヒントとトリック参照)

このように画面サイズに応じた幅を取ることができます(レスポンシブ・デザイン)。 のちほど、ビューの中でこのタグが使われます。

動作確認

Bootstrapの効果を確認するために、初期画面のみ実装してみます。 ルーティング、コントローラ、ビューの3つが必要です。

config/routers.rbroot "words#index"の1行を加えます。

Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  # root "articles#index"
  root "words#index"
end

コントローラWordsを作ります。 アクションは初期画面がindexで、AppendからListまではそれぞれ同名小文字にします。 railsのgenerateコマンドで作成します。

$ bundle exec rails generate controller Words index append change delete search
      create  app/controllers/words_controller.rb
       route  get 'words/index'
              get 'words/append'
              get 'words/change'
              get 'words/delete'
              get 'words/search'
      invoke  erb
      create    app/views/words
      create    app/views/words/index.html.erb
      create    app/views/words/append.html.erb
      create    app/views/words/change.html.erb
      create    app/views/words/delete.html.erb
      create    app/views/words/search.html.erb
      invoke  test_unit
      create    test/controllers/words_controller_test.rb
      invoke  helper
      create    app/helpers/words_helper.rb
      invoke    test_unit

コントローラの内容変更は今のところありません。

ビューを作ります。 app/views/words/index.html.erbを編集します。

<div class="col-sm-10 col-lg-8 col-xl-6 mx-auto">
  <%= render "navbar" %>
  <h1 class="text-center">単語帳</h1>
  <h5>単語帳の使い方</h5>
  <ul class="list-group">
    <li class="list-group-item">Wordbook: 初期画面に戻ります</li>
    <li class="list-group-item">Append: 単語を追加します</li>
    <li class="list-group-item">Change: 登録済み単語を変更します</li>
    <li class="list-group-item">Delete: 単語を削除します</li>
    <li class="list-group-item">Search: 単語を検索しマッチした単語を表示します</li>
  </ul>
</div>

以上の準備をしたら、railsでサーバを動作させてブラウザで画面確認します。 今回はbundle exec rails serverを使わずに./bin/devを使います。 これは、前者ではJavascriptが動作しないためです。 Rails Guideの「Working with JavaScript in Rails」の「2 Adding NPM Packages with JavaScript Bundlers」に./bin/devのことが書かれています。

$ ./bin/dev
21:03:34 web.1  | started with pid 50151
... ... ...
... ... ...
21:03:35 web.1  | * Listening on http://127.0.0.1:3000
21:03:35 web.1  | * Listening on http://[::1]:3000
21:03:35 web.1  | Use Ctrl-C to stop
21:03:36 css.1  | Sass is watching for changes. Press Ctrl-C to stop.
21:03:36 css.1  | 

ここでブラウザでhttp://localhost:3000を見てみます。

Word-indexの画面

コンテンツの幅がコンテナよりもかなり小さくなっていることが分かると思います。 また、上にナビゲーションバーも表示されています。

ブラウザがChromeであれば、スマホ画面でどうなるかを確かめることができます。 右上の3つドットが縦に並んでいるアイコンをクリック=>その他のツール=>デベロッパーツール、と選択します。 中央上に画面サイズ設定のメニューがあります。 そこで、IphoneSEのサイズを選んだのが次の図になります。

iPhoneSEサイズのWord-index画面

スマホ画面いっぱいにコンテンツが表示されていて、レスポンシブ・デザインが効いていることがわかります。 また、ナビゲーションバーも変化しています。 ハンバーガーメニューが右上に現れ、それをクリックするとドロップダウンメニューの中にAppendからListまでのメニューが入っています。 このような動的な動きはBootstrapのJavascriptがやっています。

まとめ

Bootstrapを用いたので労せずメニューやレスポンシブデザインを作ることができました。 BootstrapはRailsに限らず、一般のウェブサイト制作に適用することができます。 どんどん使ってみてください。

次回はいよいよ単語データを登録する部分にチャレンジします。