hiyoko-programingの日記

プログラミングを勉強したてのひよっ子。   エンジニア目指して勉強中。

フロント実装 〜ヘルパーメソッド〜

以下のように3つのSNSサービスにリンクするアプリケーションを実装していく。

完成gif

各リンクは以下のページへリンクしている。

実装は以下のステップで進めていく。

  1. railsの新しいアプリケーションを作成する
  2. 仕様書を確認して、手描きでマークアップしてみる
  3. 大枠を作成する
  4. font-awesomeでアイコンを導入する

railsの新しいアプリケーションを作成

まず、アプリケーションを作成する。名称はなんでも構わないが、ここではview_drillとする。

ターミナル
1
2
$ cd ~/projects
$ rails _5.2.3_ new view_drill -d mysql

view_drillディレクトリに移動し、データベースも作成しておく。

ターミナル
1
$ rails db:create

hamlの実装

次にrailsにおいてhamlを使用するためのgemを導入する。今回はhaml-railsを用いる。

haml-rails 公式ドキュメントにおいて、read.meにgemの使用方法等がまとめられている。

To use it, add this line to your Gemfile...

とあるので、記載どおりgemファイルに導入してみる。

Gemfile
1
2
gem "haml-rails", ">= 1.0", '<= 2.0.1'
# 最下部に記載

記載したらbundle install。

ターミナル
1
$ bundle install

これでhaml使用の準備が整った。次に、rails new時に作成される、既存のerbファイルをhamlに変換する。公式ドキュメントを読むと

Converting all .erb views to haml format
If you want to convert all of your .erb views into .haml, you can do so using the following command...

と書いてあるので、コマンドを実行。

ターミナル
1
$ rails haml:erb2haml

途中で

ターミナル
1
Would you like to delete the original .erb files? (This is not recommended unless you are under version control.) (y/n)

と聞かれますが、今回はアプリケーションの初期段階であるため、yを選択。すると、デフォルトで作成されたerbファイル(例えば、application.html.erbなど)がhamlに変換されていることが確認できる。

コントローラー・ビュー・ルーティングの作成

続いて、コントローラーを作成する。コントローラー名はなんでも構わないが、今回はposts_controllerを作成することにする。

ターミナル
1
$ rails g controller posts

次に、indexアクションを記載しておく。

posts_controller.rb
1
2
3
4
class PostsController < ApplicationController
  def index
  end
end

続いて、app/views/posts/index.html.haml を作成し、確認用の文字列を入力しておく。

posts/index.html.haml
1
hello haml!

ルーティングの設定をしておく。今回はroot_pathにアクセスした場合に、posts#indexが呼び出されるようにする。

routes.rb
1
2
3
Rails.application.routes.draw do
  root to: 'posts#index'
end

Sassの導入

今回はscssのみ利用するため、 application.cssは削除し、application.scssを作成する。また、posts.scssがすでに作成されているが、_posts.scssにファイル名を修正する。そしてapplication.scssで以下のように_posts.scssを読み込む記述をする。

application.scss
1
@import "posts";

また、リセットCSSの導入も行う。リセットCSSにはいくつかあるが、今回はYUI3というリセットCSSを利用する。以下のページからダウンロードが可能。

YUI3

https://github.com/yui/yui3/releases

Source code(zip)をダウンロードしたら、yui3-3.18.1/src/cssreset/css/cssreset.cssを開く(YUI3のバージョンは異なることがある)。そちらを全てコピー。

_reset.scssを作成し先程コピーしたコードを貼り付ける。以下のようになるはずです

_reset.scss
1 2  3  4  5  6
7  8  9 10 11 12
13 14
15 16 17 18
19
20 21
22 23 24 25 26
27 28 29
30 31 32
33 34
35 36
37
38
39
40
41
42
43 44
45 46
47 48
49 50
51 52
53 54
55
56 57 58
59 60
61 62
63 64
65 66
67 68
69 70
71 72
73 74
75 76
77 78
79 80
81 82
83 84
85 86
87 88
89 90
91 92
93 94
95 96 97
98 9900
01
02
03
04
05
06
07
08
09
10
11
12
1314
/*
    TODO will need to remove settings on HTML since we can't namespace it.
    TODO with the prefix, should I group by selector or property for weight savings?
*/
html{
    color:#000;
    background:#FFF;
}
/*
    TODO remove settings on BODY since we can't namespace it.
*/
/*
    TODO test putting a class on HEAD.
        - Fails on FF.
*/
body,
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
code,
form,
fieldset,
legend,
input,
textarea,
p,
blockquote,
th,
td {
    margin:0;
    padding:0;
}
table {
    border-collapse:collapse;
    border-spacing:0;
}
fieldset,
img {
    border:0;
}
/*
    TODO think about hanlding inheritence differently, maybe letting IE6 fail a bit...
*/
address,
caption,
cite,
code,
dfn,
em,
strong,
th,
var {
    font-style:normal;
    font-weight:normal;
}

ol,
ul {
    list-style:none;
}

caption,
th {
    text-align:left;
}
h1,
h2,
h3,
h4,
h5,
h6 {
    font-size:100%;
    font-weight:normal;
}
q:before,
q:after {
    content:'';
}
abbr,
acronym {
    border:0;
    font-variant:normal;
}
/* to preserve line-height and selector appearance */
sup {
    vertical-align:text-top;
}
sub {
    vertical-align:text-bottom;
}
input,
textarea,
select {
    font-family:inherit;
    font-size:inherit;
    font-weight:inherit;
    *font-size:100%; /*to enable resizing for IE*/
}
/*because legend doesn't inherit in IE */
legend {
    color:#000;
}

application.scssで以下のように_reset.scssを読み込む記述をする。

application.scss
1
2
@import "reset";
@import "posts";

この時、フォルダ構造はこの様になっているはず。
image.png

確認する

上記の準備が完了したら、rails sをして確認用の文字列が表示されているか確認。
image.png

上記が確認できたら、準備は完了。いよいよフロントの実装に入る。

仕様書を確認して、手描きでマークアップしてみる

まず、どのような見た目を実装するのか、仕様書を確認する。

仕様書

こちらを元に、手描きで大まかにブロックやエレメントを分けてみる。例えば以下のような形である。
IMG_0770.JPG

大枠を作成する

実際にHamlを書いていく。まずは、細かい点を気にせずに、以下のように全体的に構造を決める。

index.html.haml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.header
  .header__logo
    SNS
.contents
  .contents__message
    follow me!
  .contents__btn
    Facebook
  .contents__btn
    Twitter
  .contents__btn
    Instagram

当然、この状態だと以下の様に縦に並んでいるだけ。
image.png

続いて、リンク部分について考える。htmlではリンクはaタグを使うが、Railsではヘルパーメソッドのlink_toを使用することが一般的。

html.erbでヘルパーメソッドやRubyの記述をする際は、erbタグ(<% %>や<%= %>)を用いていた。erbからhamlに変更したので、ヘルパーメソッドの表記は以下のようになる。

index.html.haml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.header
  .header__logo
    SNS
.contents
  .contents__message
    follow me!
  = link_to "https://www.facebook.com/", class: "contents__btn" do
    Facebook
  = link_to "https://twitter.com/", class: "contents__btn" do
    Twitter
  = link_to "https://www.instagram.com/", class: "contents__btn" do
    Instagram

ヘルパーメソッドのクラスの設定など、オプションについて忘れてしまった際は、以下のドキュメントを確認すると良い。
Rails APIドキュメント

https://api.rubyonrails.org/v5.0/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to

ブラウザで確認すると、リンクに変わったことがわかる。
image.png
しかし、これまで縦に並んでいたSNSへのリンクが横並びになった。

Font Awesomeでアイコンを導入

続いてアイコンを導入する。 Font Awesomeというサービスを利用。
Font Awesome

https://fontawesome.com/

通常の使用方法について多くの参考記事が存在するが、Font Awesomeに対応するgem font-awesome-sassが存在する。今回はRailsでの開発になるため、こちらのgemを使用する。

公式のgithubを確認。
font-awesome-sass

https://github.com/FortAwesome/font-awesome-sass

read.meに記載されている通り、以下の様にgemを導入する。

Gemfile
1
2
gem "font-awesome-sass"
# 最下部に記載

忘れずにbundle installする。
続いて、application.scssに以下の様に記述。

application.scss
1
2
3
4
@import "reset";
@import "font-awesome-sprockets";
@import "font-awesome";
@import "posts";

続いて、実際にhamlの中にアイコンを導入する記述をしていく。公式githubを確認すると以下のように記載することでアイコンが反映されるとある。

index.html.haml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.header
  .header__logo
    SNS
.contents
  .contents__message
    follow me!
  = link_to "https://www.facebook.com/", class: "contents__btn" do
    = icon('fab', 'facebook-square')
    Facebook
  = link_to "https://twitter.com/", class: "contents__btn" do
    = icon('fab', 'twitter')
    Twitter
  = link_to "https://www.instagram.com/", class: "contents__btn" do
    = icon('fab', 'instagram')
    Instagram

gemを用いることでfontawsomeアイコンをヘルパーメソッドで導入することができる。アイコンの名称等は

fontawesomeの公式Webページ

https://fontawesome.com/icons?d=gallery

から確認することができる。

実際にどのような見た目になっているか確認。
image.png

これでアイコンを導入することができた。

SassでCSSを書く(大枠を整える)

実際に仕様書を確認しながら、CSSで見た目を整えていく。まずは、大枠を整える必要がある。まずはヘッダー部分については以下のように記載。

_posts.scss
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.header {
  height: 90px;
  border-bottom:20px solid #ffa500;
  background-color: #ff8c00;
  color: #ffffff;
  &__logo {
    text-align: center;
    font-size: 36px;
    line-height: 90px;
  }
}

ヘッダー下の20px幅の部分については、今回はborder-bottomで表現することにした。この段階で一度ブラウザで見た目を確認しておく。

次に、コンテンツ部分を作成していきます。まずは大枠を整える。

_posts.scss
12
13
14
15
16
17
18
// headerについての記述は省略
.contents {
  height: calc(100vh - 90px - 20px);
  width:900px;
  margin: 0 auto;
  background-color: #fffacd;
}

heightについては、100vhからヘッダー分の高さ(90pxとヘッダー下部の20px分)を引いた数値で表現している。

次に、"follow me"の文字列部分のCSSを記載する。

_posts.scss
12
13
14
15
16
17
18
19
20
21
22
23
24
// headerについての記述は省略
.contents {
  height: calc(100vh - 90px - 20px);
  width:900px;
  margin: 0 auto;
  background-color: #fffacd;
  &__message{
    padding: 40px;
    color:#ffa500;
    font-size:30px;
    text-align: center;
  }
}

この段階では以下のような見た目になっているはず。
image.png

SassでCSSを書こう(aタグの取り扱い)

検証ツールを用いて確認すると、link_toタグで表記している部分はaタグに読み替えられてブラウザで表示される。
image.png

aタグはインライン要素であるため、今回のように縦に並べたい場合はブロックレベル要素へ変換する必要がある。そこで以下のような記述を行う。

_posts.scss
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// headerについての記述は省略
.contents {
  height: calc(100vh - 90px - 20px);
  width:900px;
  margin: 0 auto;
  background-color: #fffacd;
  &__message{
    padding: 40px;
    color:#ffa500;
    font-size:30px;
    text-align: center;
  }
  &__btn{
    display: block;
  }
}

こうするとブロックレベル要素として取り扱うことができるので、縦に並べることができる。
image.png

続いて、HTMLの記述も修正する。なぜならば、各SNSごとにボタンの色が異なるから。アイコンにもクラスを付与しておく。

index.html.haml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.header
  .header__logo
    SNS
.contents
  .contents__message
    follow me!!
  = link_to "https://www.facebook.com/", class: "contents__btn contents__btn--facebook" do
    = icon('fab', 'facebook-square',  class: "contents__btn--icon")
    Facebook
  = link_to "https://twitter.com/", class: "contents__btn contents__btn--twitter" do
    = icon('fab', 'twitter', class: "contents__btn--icon")
    Twitter
  = link_to "https://www.instagram.com/", class: "contents__btn contents__btn--instagram" do
    = icon('fab', 'instagram', class: "contents__btn--icon")
    Instagram

cssを以下のように記載していく。

_posts.scss
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Header部分は省略
.contents {
  height: calc(100vh - 90px - 20px);
  width:900px;
  margin: 0 auto;
  background-color: #fffacd;
  &__message{
    padding: 40px;
    color:#ffa500;
    font-size:30px;
    text-align: center;
  }
  &__btn{
    display: block;
    height: 80px;
    width :360px;
    margin:0 auto 50px;
    border-radius: 10px;
    font-size:20px;
    color:#fff;
    text-decoration: none;
    text-align: center;
    line-height: 80px;
    &--icon{
      padding-right:20px;
    }
    &--facebook{
      background-color: #4169e1;
    }
    &--twitter{
      background-color: #00bfff;
    }
    &--instagram{
      background-color: #fc76bf;
    }
  }
}

これでほぼ見た目は完成したはず。特に難しいcssプロパティは使用していませんが、アンパサンド(&)を使用しているため一見判断がつきにくい部分もあるかもしれない。しかし、scssにおける記述ではアンパサンドの使用は必要不可欠。

最後の微調整を行う

現状、一点だけ仕様書通りになっていない部分がある。アイコンとテキストの隙間に注目してみると、padding分の20pxに加えて、隙間が存在してしまっていることがわかる。
image.png

これはhamlで改行して記載しているために生じる隙間。これを埋めるためにはどの様にすれば良いのか?

この解決方法はhaml公式ドキュメントに記載がある。

haml公式ドキュメント Whitespace Removal

要は<>を付与することでこの隙間を消すことができる。以下の様にspanタグで区切った後に<>を付与してみました。

index.html.haml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
.header
  .header__logo
    SNS
.contents
  .contents__message
    follow me!!
  = link_to "https://www.facebook.com/", class: "contents__btn contents__btn--facebook" do
    = icon('fab', 'facebook-square',  class: "contents__btn--icon")
    %span<>
      Facebook
  = link_to "https://twitter.com/", class: "contents__btn contents__btn--twitter" do
    = icon('fab', 'twitter', class: "contents__btn--icon")
    %span<>
      Twitter
  = link_to "https://www.instagram.com/", class: "contents__btn contents__btn--instagram" do
    = icon('fab', 'instagram', class: "contents__btn--icon")
    %span<>
      Instagram

すると以下の様に隙間を消すことができ、仕様書通りに作成することができる。
image.png