20200414のHTMLに関する記事は12件です。

HTML / CSS から Nuxt.js で webアプリを開発するまでのロードマップ

はじめに

この記事では、HTML / CSS から学び、Nuxt.js で webアプリを開発するまでの過程を紹介します。

本記事は、以下のような方が対象です。同じ初学者を意識しています。

  1. web開発に興味がある。
  2. HTML / CSS を学んだが、そこからどうすれば良いか謎。不可思議。
  3. フレームワークとは何ぞや。フレームワークのフレームワーク?
  4. どこまでやれば、どういうものができるのか具体的に知りたい。
  5. とりあえず、web開発に興味はあるよ。

その道の方でも、「趣味で学ぼうとする人は、こういう学び方をしているんだな」と参考になるかもしれません。

筆者であるうたかたは、web開発を始めて早2ヶ月ですが、わからないことでいっぱいです。基本的に表面的な理解に終始しています。ただ「初学者でも、今あるものを使うと、こういうものができるよ」という具体例とその学習過程を紹介することで、何か少しでもお役に立てたらと思います。Qiita も利用し始めたばかりなので、何かありましたら色々とご指摘いただけると助かります。

開発中の音楽SNS「UTAKATA」の紹介・解説動画を YouTube に投稿しているので、「この記事で紹介しているもの(Vue.js・Vuetify.js・Nuxt.js・Firebase)を学ぶと、少なくともこういう webアプリを作ることができるよ」という具体例として参考にしていただければと思います。近日中にベータ版(?)を公開する予定です。

最後に、本記事は各言語・フレームワークについて詳細な解説をするものではありません。「何を学べば良いかわからない」「何を学べば、何ができるかわからない」という方に、一つの指針を提供することが目的です。

少し前置きが長くなりましたが、最後までお読みいただけると嬉しいです。

ロードマップ

これまでの学習の軌跡を振り返りつつ、web開発のロードマップを紹介します。

学習の流れは、HTML / CSS → JavaScript → Vue.js & Firebase → Vuetify.js → Nuxt.js でした。

学習期間と内容は、概ね以下の通りです。

  • 前半(1ヶ月):HTML / CSS / JavaScript の基本を学び、その後何を学べば良いか調べてた。もはや「web開発」とは何ぞや状態。そして今も。
  • 後半(1ヶ月):Vue.js → Firebase → Vuetify.js → Nuxt.js の存在を辿り、動画を見て、公式ドキュメントを見ながらコードを書いた。

主に前半は The Net Ninja さんの動画(YouTube & Udemy)、後半は公式ドキュメントを頼りにしています。

The Net Ninja さんについては、偶然見つけただけで、特に深い理由はありません。運命の出会いでした。英語ですが、とてもわかりやすいです。このわかりやすさに慣れると、少しつらいものもあります。ストリートファイターの春麗(Chun Li)が好きみたい。マリオも出てくる。ほぼ全て無料で見ることができるのには、感謝しかないです。謝謝。

「どこまでやれば、どういうものができるのか」が想像もできなかったので、一度動画を(コードを書かないで)見るだけみて、Nuxt.js に行き着いてから、公式ドキュメントを参照しつつ、コードを書き始めました。

この記事を参考にする場合も、一度最後まで読み通して見通しをつけてから、各言語・フレームワークを学習し始めると良いと思います。知らない用語が出ても、「そういうものがあるんだね」と思って進んでください。知らないことばかりですもん。

学習項目は以下の通りです。

  • HTML
  • CSS
  • JavaScript
  • Vue.js:JavaScript のフレームワーク。
  • Vuetify.js:Vue.js のデザイン担当。
  • Nuxt.js:Vue.js の開発環境・機能強化担当。
  • Firebase:ユーザー認証(登録・ログイン)やデータベースを管理。

1. HTML / CSS

自分は「Flexbox とは何ぞや」という人間だったので、以下の動画も見ました。あとで紹介する Vuetify.js で採用されている Grid システムをそのまま理解することができます。

動画が洗練されていくのを肌で感じることができるのは、複数見た人の特権です。

今回紹介している動画は有料(Udemy)2つを含みますが、それ以外はほぼ全て無料なので、ぜひ覗いてみてください。

最近では、Firebase Functions に関するシリーズ(無料)が投稿されていたようです。まだ見ていませんが、きっと参考になると思います。

2. JavaScript

フルバージョンは Udemy(有料) で公開されています。動画の説明欄にクーポン付きのリンクがあるので、そちらからご利用ください。

基本的な文法(「こうやって書くよ」)・関数(「こういうことがしたい時、こういう機能があるよ」)・概念(「こういう考え方で、コードを組み立てるよ」)を一通り学ぶことができます。

学習の指針としては、動画を見て基本的な概念を理解しつつ、文法・関数(の存在)を何となく知っておきます。実際にコードを書く段階で、その関数名やしたいことで検索して、例を見ながら書きます。「検索できるようになる」こと、そして「書かれている内容を理解できるようになる」ことが大事だと思います。全てのコードを覚えておく必要はありません。未だに何でも調べてます。

この後も基本的に、動画を見て何となく理解する → ドキュメントを参照してコードを書くという流れです。

3. Vue.js & Firebase

Udemy(有料)で公開されている動画の紹介動画です。動画の説明欄にクーポン付きのリンクがあるので、そちらからご利用ください。

web開発を学ぶのにかけた費用は、以上の動画2つ分(約2400円)です。
安いと思います。感謝しかないです。

Vuex については、以下の動画(無料)を参考にしました。

Vue については、以下のサイトも参考になりました。Vuex についての記事を載せておきます。目を通せてはいないですが、初学者にも、とてもわかりやすい解説です。謝謝。

一応、Firebase [Firestore/Storage] セキュリティルールの具体例を以下の記事で紹介しています。とても参考になったブログ・記事についても触れているので、いつの日か覗いてみてください。喜びます。

4. Vuetify.js

使用しているコンポーネント(デザインを形作るパーツ。Buttonコンポーネントなど)や使い方が古い場合もありましたが、コメント欄で有志により全て補足されていました。

コードを書くときには公式ドキュメントの例を見ながら書くので、まずは動画で雰囲気を掴めれば良いと思います。その点で、とても参考になりました。

5. Nuxt.js

存在は Vuetify.js の公式ドキュメントで知りました。

Nuxt.js については、はじめから公式ドキュメントのガイドに沿って何となく理解していきました。一応、Vue School の動画も見たと思います。

Vue.js・Vuetify.js・Nuxt.js・Firebase は、いずれも日本語の公式ドキュメントが充実しているので、動画を見て雰囲気を掴んだら、そのまま公式ドキュメントを読むと何となく理解できます。どんなにわかりやすいサイトがあっても、参照しているのは最終的に公式ドキュメントのはずなので、それを読むことで安心感を得るのです。公式ドキュメントのわかりにくいところは、検索すれば先人が知恵を貸してくれます。

おわりに

この記事では、HTML / CSS から学び、Nuxt.js で webアプリを開発するまでの過程を紹介してきました。

「何があるかわからない」「何から手をつけていいかわからない」という方の参考になれたら幸いです。

web開発を学ぶ上で、数多くのサイト・記事を参考にさせていただいています。本記事では全て列挙することはできませんが、この場を借りてお礼申し上げます。いつもありがとう。謝謝。

趣味で NCS 等の和訳を YouTube に投稿しているので、よければそちらもご覧ください。
YouTube チャンネル:うたかた / UTAKATA

少し長くなりましたが、ここまで読んでいただきありがとうございました。ではでは!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML、CSSで忘れそうだけど押さえておきたいこと

CSS

HTMLと別のファイルで書くのが基本。しかしHTMLの要素に直接書き込みこともある。例えば最初はdisplay:none;にしておく要素など。

<p style="display:none;">見えない</p>

大きさの単位

絶対的な大きさとしてpxが主流。しかしスマホの普及により、rem(html要素のフォントを1とした時の相対的な単位)を使用される

色は#FFFFFFやrgb(255,255,255)などの方法で表される。
カラーコードでぐぐるとわかる

idとclass

idは一度しか定義できず、CSSで読みだすときは#をつける。
classは複数回定義できるが、CSSで読みだすときは.をつける。またclassは複数所属可能なので、class = aaa bbbとしてやるとaaaとbbbに所属できる。

優先度

親要素でCSSの内容を記述し、子要素でもCSSの内容を記述したとき子要素の内容が優先される
同じ要素のidのCSSの内容とclassのCSSの内容ならば、idのCSSの内容が優先される

詳しくはCSSセレクタの優先度に関してググると良い

ボックスモデル

HTMLの要素はpadding border marginで囲まれている。
HTMLの要素自体の幅、高さをwidth、heightで表す

要素.jpg

またその要素に
box-sizing: border-box;
と指定すると、widthやheightをmarignまで含んだ時の大きさに設定することになる

インラインとブロック

ブロック要素

ブロックは画面の横幅全体を使い、次の要素は改行された状態になる。
例 h1~h6 p form table ul ol dl li div

text-alignやmarginをいじるやインラインブロックへの変更をすることでブロック要素は配置設定に使用できる(インライン要素は配置設定をしないのは基本。)

インライン要素

インラインは画面の横幅全体を使わず、次の要素は隣にくるようになる。
例 br a img input select textarea strong span

上述したがインライン要素は配置設定をしない。

そもそも下記なので配置設定できない。
・大きさ制限できない(width,height)
・marginできない(ホントは左右はできる)
・text-alignできない
・floatできない

またインライン要素の配置を変えたいならば、その要素に親要素を作り、そいつのtext-alignやmarginをいじるやインラインブロックへの変更をすることで配置設定をする。そのためインライン要素から別の要素への変換(display)をする機会はない。

インラインブロック要素

インラインブロックは基本的にブロック要素的な面が強いが、次の要素は隣にくるようになる。ブロック要素だけでは配置設定のバラエティーがなくなるので、ブロック要素からインラインブロック要素に変更することがある。ブロック→インラインブロックのパターンしかない。

重要

以上のことからわかることは
配置変更をしたければ、ブロック要素をいじることで行う。例えば、text-alignやmargin変更、インラインブロックへの変更で横に大きいの並べるとか

text-alignとmargin

私はtext-alignとmargin 0 autoが同じだと思っていた。でも違うのだ。

text-alignはその要素の幅の範囲内(width内)での文字の位置を指定している。つまり文字の位置を指定しているのである。

marginはその要素のmargin(余白)の部分を変更しているので、要素の幅(width)や高さ(height)の範囲には影響を与えない。

要するにtext-alignは要素の中での指定、marginは要素の外側での指定。

marginのあれこれ

同じ方面でmarginがかぶると、marginの相殺(margin同士の足し算ではなく、marginの大きい方を採用する)。marginの相殺と呼ぶ
margin 0 autoで中央ぞろえ
margin-left autoでベタッリ右

要素の真ん中に文字をおきたければ

要素の大きさ(幅や高さ)のwidthやheightで行う。要素の大きさを変えても要素内での文字の位置は変わらない。

要素内での文字の位置を変えたければ、text-align: center;で文字を幅でも高さでも真ん中に置くのかを決める。またline-heightで文字自体の高さを決めれるので、要素のheightとline-heightを同じにすることで、完璧に要素の真ん中にもじが来るようになる。

範囲指定

範囲を指定するためだけの要素

ブロックで範囲を指定するのがdiv(text-alignやmarginでいじって、指定範囲の位置指定)

インラインで範囲をしてするのがspan(特定の文字の色変更とかに使用)

background

backgroundを使用すると背景を変えれる。backgroundでの範囲はその要素のborderまでの範囲なので、余白をつけたければ,marginを使う。またrepeatを指定すると、永遠にその背景ってことになる。

img

imgはインライン要素なので、pで囲って無理やりブロックにして、配置指定をする。

position

配置指定でたまに使うのposition。こちらのサイトが非常にわかりやすかったです。CSSのpositionを総まとめ!absoluteやfixedの使い方は?

CSSの開発の仕方

BEMではclass名を長くして、名前が被らないように開発する。あとで書き直すときに書き直しやすい。Sassのおかげで書くのが楽になった。

OOCSSではオブジェクト志向でCSSを記述するので、一つの要素が複数のclassに所属している状態になる。配置や構造を示すclassとデザインを示すclassに所属するイメージ。デザインが一貫しているWEBを作りたいならこちらの様式でやる

Sass

@ mixin  名前A('引数') {
}

#名前B {
@include  名前A('引数')
}

//このようにcssをクラスのように扱えるようになる

参考文献はSCSSの基本的な書き方

HTML

<>で囲まれている部分を要素idやnameなどは属性という。

h1やpで囲んで記事を書くが、なくても文字は表示される

imgのalt属性は画像が表示されない時に出る文字、最近はSEOに必要な要素にもなっている?

ulは箇条書き(順序なし)、olは箇条書き(順序あり)、dlは説明って感じ(dlの中にdt(用語)、ddが用語の詳細説明)

表に関して。
まず

で表が作成できる準備をする

そいつの子要素としてtrで行をざっくり作る、trの中にthやtdを入れて行にマスを作っていくイメージ。thは見出し、tdは左寄せ。

bodyの構造。headerとfooterを頭とお尻に。その間の空間をarticleとして、その中に複数のsection(切りの良いところで、次のセクションに次の内容を書く)を置く。最近はSEO的にも重要だとか

form_tagとform_forの違い。モデルに対して操作を行うならば、form_forを使い、そうでないならばform_tagを使用する。詳しくはrailsで学ぶサーバーサイド

またform_tagでmethodを指定するが、postは見えない状態で、paramsの中に入れて運ぶ感じ。getではURLに?でクエリ(条件)を置いて、送信する。詳しくはrailsで学ぶサーバーサイド

<a>は外部のURLにアクセスするための要素
<%= link_to %>はアプリ内部のrootingにアクセスするために作られた関数

nameとid属性の違い
id属性はCSSやJSで使用する。nameはサーバーサイドでparamsから値を得る時に使用する。

viewでの注意

・お作法としてapplication.htmlには統一レイアウトを置き
yeildという場所を書き、そこに全てのviewの内容が反映される。

app/views/layouts/application.html.erb
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all',
                               'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application',
                               'data-turbolinks-track': 'reload' %>
    <%= render 'layouts/shim' %>
  </head>
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
      <%= render 'layouts/footer' %>
    </div>
  </body>
</html>

同様に全てのviewは、別のviewファイルから読みだして自身のviewに反映させてもいい

<%= render '反映させたいviewの場所' %>
<%= render @microposts %>
#これをするとviewの中のmicroposts/_micropost.html.erbを追加することになる

これをパーシャルを追加するという。他にも

<%= render 'shared/error_messages', object: f.object %>
#これを行うとshared/error_messagesの方で、f.object(つまりf自身、入力内容自身)を変数objectと設定してパーシャリ川で使用できるという事

_error_messages.html.erb
<% if object.errors.any? %> #このようにして使用する
.
.
.

これも同じような意味

this.erb
<%= render partial: "list_footer", locals: {username: @username, listname: @listname} %>

この意味はlist_footerパーシャル(view)の中でthis.erbで定義した@usernameをusername、 @listnameをlistnameとして使用できるという事。しかしloacalsを使わずに、@usernameをそのまま使用することも可能。しかしlocalsを使った方がパーシャルを再利用しやすいため使用される。参考文献は部分テンプレートにlocals:を使って変数を渡す時は partial:もつけないとエラー

・erb(rubyの埋め込まれたHTML)
erbでは<% 中身 %>で中身の処理をして、<%= 中身 %>では中身の処理をして結果をHTMLに出す。

app/views/static_pages/home.html.erb
<% provide(:title, "Home") %> #ここで:titleにHomeを代入
<!DOCTYPE html>
<html>
  <head> #ここで読み出して、HTMLに出す
    <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
  </head>

provideもyieldも関数で、変数に値を入れる、読み出すの処理をしてくれているということ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails 開発日記1 トップページの実装

 トップページの実装

*この記事は技術や知識の共有目的ではなく、完全に自己満足のアウトプット用です。

<開発環境>
1. ruby 2.6.3
2. Rails 5.1.6
3. AWS Cloud9
4. GitHub
5. Heroku(予定)

SNSのコピーサイトの開発にあたり、トップページを実装をした。
スクリーンショット 2020-04-14 16.14.22.png
                 1、トップページの完成系

自分なりに工夫した点

1.パーシャルの使用
パーシャルは開発時にHTMLの中身を整理する為のものである。
Railsチュートリアルを参考に、headerとIE用のHTML shimをまとめてみた。

<!DOCTYPE html>
<html>
  <head>
    <title>JiroApp</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= render 'layouts/shim' %>
  </head>

  <body>
    <%= render 'layouts/header' %>
    <%= yield %>
  </body>
</html>

これによって、headerのHTMLを <_header.html.erb> に移行することができました。

HTML CSSはあまり得意ではないのでこだわるつもりはないです。。。

次はユーザー登録機能の実装をしていきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cookieを再設定するとexpiresの値が削除される

cookie操作時に気づいたので共有します。

以下のようなexpires(有効期限)の設定されたcookie情報があった場合に
key=valA; expires=2020-04-15T14:00:00.000Z;

以下のように値だけ入れ直したところ
document.cookie = 'key=valB;'

予想では以下のように値だけ変更される想定でしたが、
key=valB; expires=2020-04-15T14:00:00.000Z;

実際には以下のように値は変更されるが、
同時に有効期限の値が失われてしまった
key=valB;

なので、有効期限を設定されていた場合は、
以下のように値だけの更新であっても、有効期限も設定し直す必要があることが分かった
document.cookie = key=valB; expires=2020-04-15T14:00:00.000Z;

以上。
あまり、この事に触れた記事を見かけたことがなかったので投稿してみました。
ご参考まで!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

divとspanの違いってなんですか??

HTMLのdivタグとspanタグの違い、以前受講生に質問されて苦い思いをしました:confounded::sweat_drops:
この機会にマスターします!

1. 何度でも復習!そもそもタグってなに?

HTMLは、とにかくタグでテキストを挟むのが大好き!変態や...!
開始タグ終了タグで挟まれたテキストは、そのタグの命令を聞かなければいけない:scream:
つまり、挟むタグの種類によって、なんの変哲もないテキストの役割は変わるのです。

書き方: <タグ名>テキスト</タグ名>

2. でも注意!divもspanも意味を持たないタグ!?

先ほど述べたように、タグは種類によって役割が違います。
例えば・・・
・pタグなら段落を表す
・h1タグなら見出しを表す
だけどdivタグとspanタグときたら、
彼らはタグ自体に意味を持たない、つまり何も表さないタグらしいんです!

3. じゃあ一体どんな役割があるのかな?

:star:divタグ編
結論、divタグは囲った部分を、ブロック要素としてまとめて扱うことができるのです:point_up:

どうゆうことかというと・・・

<h1>渡邉貴恵とは</h1>
<p>私は福島県出身の24歳です</p>
<p>皆さんよろしくお願いします!</p>

というコードを、<h1>タグと<P>タグごとに一つのブロック=まとまりとして認識して色分けを行いたい時・・・

<div class="one">
<h1>渡邉貴恵とは</h1>
</div>

<div class="two">
<p>私は福島県出身の24歳です</p>
<p>皆さんよろしくお願いします!</p>
</div>

こんな風に、divタグを使ってまとめることで、各々に違うcssを当てることができるようになるのです!
またdivタグでまとめた部分はmarginやpaddingなどのレイアウトや、width、heightなどのサイズも自由に決めることができるみたい!
確かに思い返せばそうだったなメモメモ...:pencil2:

:star:spanタグ編
divタグではブロック要素ごとに認識され、レイアウトやサイズなどの装飾を加えることができたけど・・・
spanタグでは行の一部ごとのまとまり、つまりインライン要素として認識され、その各々にcssを当てることができるのです:point_up:

詳しく見て行きましょう
例えば・・・

< 私の名前は"渡邉貴恵"でございます >

この"渡邉貴恵"の部分だけを赤く装飾したいよー!って時に、

 <p>私の名前は
   <span class="aka">渡邉貴恵</span>
  でございます</p>

こんな風に装飾したい行の一部分だけをspanタグで囲めば、
なんと!!!
.aka{color:red}
ほら、これでようやく愛しのcssを当てて赤く装飾することができるでしょ:sparkles:

つまり<span class="クラス名"> テキスト </span>で挟んだテキストだけをピックアップして、文字の色や太さを変えたい時に使えるよ!


:hatching_chick:まとめ:hatching_chick:
divタグは、テキストを挟むことで大きなまとまりとしてブロック化(=ブロック要素)し認識させることで、レイアウトやサイズを指定をすることができる。
spanタグは、テキストの行の一部分を挟むことで小さな行のまとまり(=インライン要素)として認識し、文字の色や太さの装飾を加えることができる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

liタグの中のaタグ要素を縦方向にセンタリングする方法についての質問

liタグで作ったaタグ要素のテキストをborderの大きさに合わせて縦方向にセンタリングしたいのですが、上寄せとなってしまいます。
よろしくお願い致します。

<head>
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha256-UzFD2WYH2U1dQpKDjjZK72VtPeWP50NoJjd26rnAdUI=" crossorigin="anonymous" />
</head><!--font-awesomeのCDN-->

<div class="class_name">
 <li>
  <a><i class="far fa-question-circle"></i>テキストテキスト</a>
 </li>
</div>
.class_name {
  li {
    border: 1px solid grey;
    border-radius: 3px;
    list-style: none;
    width: 80%;
    height: 50px;
    margin: auto;
    text-align: left;
    padding-inline-start: 0px;
    margin-bottom: 5px;
  }
  i{
    color:blue;
  }
  a{
    font-size:18px;
  }
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者によるプログラミング学習ログ 290日目

100日チャレンジの290日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
290日目は、

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IPアドレスについて学びましょう!

サーバーに関する備忘録として記述します。

サーバーを理解しよう!

IPアドレスを理解するにはサーバーについて理解する必要があります。

サーバーとは?

サーバーとは、サービスを提供するコンピュータのことやコンピュータにある様々な機能のことです。もっと簡単に言うと、ある特化した機能を使うためだけのパソコン、または何かに特化した機能自体の事だと思って下さい。

サーバーが必要な理由

前提として、自身のパソコンだけでもサーバーが行うことを全て行うことが可能です。
それを踏まえた上で、サーバーが必要である理由が以下の3点となります。

・サーバーをバックアップとしてデータを保存しておけば、自身のパソコンが壊れた際にデータを復旧することができる。
・外部のクライアントと接続する機能をサーバーに任せることによって、自身のパソコンの負担を減らすことができる。
・上述の接続機能の例のように、サーバーと自身のパソコンで役割分担をすることによって、自身のパソコンでアプリケーションの開発に集中することができる。

サーバーの役割

サーバーの役割は様々ですが、主に以下のような役割があります。

・様々なデータを管理・保存をする(データベースサーバー)
・パソコン同士の通信を行う(WEBサーバー)

様々なデータを管理・保存する役割はデータベースサーバーが担います。Webアプリケーションとは違うサーバーでデータベースの管理を行うことによってサーバーへの負担を減らします。

パソコン同士の通信を行う役割はWEBサーバーが担います。

Webサーバー

WEBサーバーとは、スマートフォンやパソコンから送られきたリクエストに対して、リクエストに合ったレスポンスを返す仕組みを持ったコンピュータ(ハード)やソフトウェアのことを指します。
このように WEBサーバーは、利用者の送ったリクエストに対して、情報を処理し、レスポンスをする役割を持ちます。

そして本カリキュラムではWEBサーバーが情報を貰ったり送ったりするために必要な「IPアドレス」について学習をしたいと思います。

IPアドレスとは?

IPアドレスとはコンピュータやサーバーに設置された住所のようなものです。コンピュータが他のコンピュータやサーバーにファイルやメールを送受信するときに宛先として利用されます。

IPアドレス例

具体的にどのようにコンピュータに住所を設定するのか、例を見ながら確認していきましょう。
IPアドレスは「183.79.139.228」のように表現されます。この数字の集まりが住所になります。それぞれの数字は0-255までの値を取るので、全パターンは約42億通りになります。(256の4乗)

image.png

IPアドレスの現状

現在このIPアドレスはIPv4と呼ばれるものです。近い将来IPアドレスは足りなくなると言われています。対策としてIPv6という新しいバージョンへの移行が進んでいます。IPv6ではIPアドレスを16進法で表し、例えば「2001.0db8.1234.5678.90ab.cdef.0000.0000」となります。こちらは3.4×10の38乗であるのでほぼ無限に使えます(例えば1兆は10の12乗です)。しかし、IPv6が世界全体で対応するためにはあと何年もかかるようです。

IPアドレス(IPv4)の問題点

ただしIPアドレスの個数は限られており、利用者が好き勝手に利用することはできません。そのため一般家庭で「△△光回線」などのインターネット契約をするとISP(インターネット・サービス・プロバイダ)から自動的にIPアドレスが付与されます。これは接続していないと自動的に変化していき、限りあるIPアドレスを回線利用者で使いまわしている状態になります。IPアドレスを利用するためにはISPから毎回利用許可を貰わないといけません。これでは企業や学校での利用などコンピュータの個数が変動する場合に手間がかかります。
このようなことを解決するために、グローバルIPアドレスプライベートIPアドレスという仕組みが生まれました。

グローバルIPアドレスとプライベートIPアドレス

数が限られ、制限がるIPアドレスですが、グローバルIPアドレスとプライベートIPアドレスによってその問題を解決しました。各々のが何で、どのような仕組みであるのかを学習していきましょう!グローバルIPアドレスとプライベートIPアドレスを理解するためにはルーターの知識も必要なため合わせて記述していきます。

グローバルIPアドレスとは?

インターネットに接続する際に使用されるIPアドレスのことを指します。インターネットに接続している機器のそれぞれに割り振られる番号(現在は上記したように「183.79.139.228」のような3桁の数字×4)になります。
家の住所や電話番号のようなイメージです。例えば「東京都渋谷区○○町××~×,△△マンション」はこの世に一つしかないようにグローバルIPアドレスもこの世に一つしかありません。

グローバルIPアドレスは、「△△光回線」などのインターネット契約をするとISP(インターネット・サービス・プロバイダ)から自動的にグローバルIPアドレスが1つ付与されます。また、グローバルIPアドレスのことをWAN(Wide Area Network)とも呼びます。

プライベートIPアドレスとは?

プライベートIPアドレスとは、ISPから自動的に割り振られたグローバルIPアドレスをさらに小分けにしたIPアドレスです。グローバルIPアドレスの「東京都渋谷区○○町××~×,△△マンション@@号室」の住所例で言うと「@@号室」がプライベートIPアドレスです。プライベートIPアドレスはLAN(Local Area Network)とも呼びます。

プライベートIPアドレスは、以下のような3つに分けられます。
- クラスA:10.0.0.0~10.255.255.255
- クラスB:172.16.0.0~172.31.255.255
- クラスC:192.168.0.0~192.168.255.255

クラスAは大規模ネットワーク、クラスBは中規模ネットワーク、クラスCは小規模ネットワークで使うプライベートIPアドレスです。ルーターはクラスCを使うため「192.168.0.0~192.168.255.255」の65536通りあります。ルーターとプライベートIPアドレスとグローバルIPアドレスを併用すると65536×42億個のプライベートIPアドレスが存在できるということになります。

ルーターとは?

ルーターとは、インターネットと「家庭や会社にある複数のパソコン」間で通信を行うときに必要となる機器です。グローバルIPアドレスの「東京都渋谷区○○町××~×,△△マンション」の住所例で言うと、ルーターは個人の部屋に入るためのドア(入り口)のイメージです。マンションの中に入ると住民それぞれの部屋があるように、グローバルIPアドレスからルーターを通すと、グローバルIPアドレスではなくプライベートIPアドレスの領域になります。ルーターは、グローバルIPアドレスからプライベートIPアドレスへスイッチする役割を持っています。ルーターは、グローバルIPアドレスと後述するプライベートIPアドレスの両方を持っています。

image.png

プライベートIPアドレスについて補足ですが「192.168,x,y」となっていますが「x」はルーターのメーカーを表し、「y」はそれぞれのパソコンによって違います。そのため一つのルーターが割り振れる番号は256通り(「y」の分だけ)です。また、ルーターはパソコンの再起動や回線が切れるごとに「y」を切り替える機能もあります。
ルータがyを切り替えることにより、ルーターが割り振る番号の消費を抑えています。
例えばTECH::CAMPの教室にいない人のパソコンに対して固定のプライベートIPアドレスを設定してしまうと256人しかwi-fiを繋ぐことができません。そのため、ルーターが必要に応じてプライベートIPアドレスの数を減らしたり増やしたりします。

ドメインとDNSとは?

グローバルIPアドレスとプライベートIPアドレスの使い分けによってIPアドレスの利用可能数は節約できましたが、まだ問題があります。
例えば、「52.196.209.128」といわれてもそれがTECH::CAMPのサイトであるとは分かりませんよね?
そのためにドメインとDNSが誕生しました。

ドメインとは?

ドメインとは、IPアドレスを文字列に変換したものです。TECH::CAMPのページのIPアドレスは、「52.196.209.128」です。IPアドレスで検索することでTECH::CAMPを表示することができますが、TECH::CAMPのページを見ようとする度にIPアドレスを覚えておくのは大変です。そこで、数字よりも覚えやすい文字列をIPアドレスに割り当てることにしました。これがドメインの誕生です。

またこのように「IPアドレスを文字列化する仕組み」のことをDNS(Domain Name System)と呼びます。
IPアドレスに比べドメインは無尽蔵に組み合わせられるので、IPアドレスを取得するよりも簡単で個人でも購入することができます。

URLから見るドメイン

TECH::CAMPのドメインを例にあげてみていきましょう。https://tech-camp.in/
この内ドメインに当たるのはtech-camp.inです。ドメインは、.(ドット)の前後で「tech-camp」と「in」に分けられます。
「in」をトップレベルドメインと呼びます。トップレベルドメインは、他にも「.com」や「.jp」などがあります。ドメインを取得するにはトップレベルドメインがなければいけません。
「tech-camp」をセカンドレベルドメインと呼びます。セカンドレベルドメインに関しては早いもの勝ちです。同じセカンドレベルドメインが先に使われていた場合は、使うことができません。

IPアドレスを調べてみよう

実際にドメインに紐づいたIPアドレスを確認してみましょう!ドメインからIPアドレスを調べるためにはdigというコマンドを使用します。

digコマンド

digコマンドは、ドメインを管理しているサーバー(DNSサーバー)に問い合わせをしてドメインからIPアドレスを取得し表示するコマンドです。digは、「domain information groper」の略です。

DNSサーバー

IPアドレスとドメインのそれぞれの組み合わせを管理・保存しているサーバーです。

DNSサーバーの場所

DNSサーバーは様々な場所に存在します。身近なところでいうとルーターの中に存在する可能性があります(機種によってない場合もある)。
またはIPアドレスを配布しているISP(インターネット・サービス・プロバイダ)内にもあります。

digコマンドを使ってIPアドレスを調べましょう!

ターミナルでdigコマンドでIPアドレスを調べましょう!

dig master.tech-camp.in
$ dig master.tech-camp.in

; <<>> DiG 9.8.3-P1 <<>> master.tech-camp.in
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64426
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;master.tech-camp.in.       IN  A

;; ANSWER SECTION:
master.tech-camp.in.    3265    IN  A   52.196.209.128

;; Query time: 66 msec
;; SERVER: 192.168.179.1#53(192.168.179.1)
;; WHEN: Tue Oct  4 23:49:29 2016
;; MSG SIZE  rcvd: 53

このような実行結果が返ってきます。IPアドレスは、ANSWER SECTIONという部分に記載されています。テックマスターのIPアドレスは、52.196.209.128ということがわかります。

ポートについて学習しよう

ポートとは1つの「サーバーと複数のサーバーを繋げるものです。それぞれのサーバが番号ごとに管理されていて、リクエストに応じてそれぞれの番号(つまりサーバ)へつなぐ橋渡しをする役割を持っています。ポートが割り振る番号には、0から65535まで使うことができます。しかし、0から1023までの番号はよく使われるもので予約されていて自由に使うことはできません。
WEBサーバはリクエストを受け付けたり、データを送信したりするのが主な仕事ですが、これ以外にもデータベースサーバや、SSHコマンド受け付けるためのSSHサーバ、メールを送受信するためのメールサーバなどいろいろなサーバがあります。

そこで1つのサーバで複数のサーバを起動するためにポートという仕組みがあります。

image.png

なぜポートが必要なのか?

一般的なサーバは1つのIPアドレスしか持っていないことがほとんどで、このIPアドレスをWEBサーバで使ってしまうと、他のサーバとして動作することができません。
例えば、WEBサーバなら「80」、SSHなら「22」、メールサーバなら「3306」などはすでに決まっています。既に利用されているポートは利用することができずにエラーになります。
Ruby on Railsでサーバーを起動するときは「3000」ポートを使うことが多いです。既に3000ポートが使われている場合や複数のサーバを起動したい場合は、rails server -p 3001のようにすることで別のポートでサーバを起動することができます。
このように、コンピュータが通信する場合はどのIPアドレスのどのポートに接続するか、というようにIPアドレスとポートのセットで考えます。

WEBサーバの動作をポートを使っておさらいしよう

一度WEBサーバについては説明しましたが、ここではポート踏まえてWEBサーバの動作について説明します。
WEBサーバでは一般的には以下の様な動作を1秒間に数千回行なっています。

1.「80ポート」でHTTPリクエストを受け付ける
2. リクエストに応じた処理や、要求されたファイルを探す
3. リクエストしてきたコンピュータに返送する

ここでいう「HTTP」はWEBサーバに情報を送るための通信規約、つまり約束事です。
次にHTTPについて詳しく説明したいと思います。

HTTPとは?

Webページやページ内で必要なCSS、JavaScript、画像などのファイルをWEBサーバーにリクエストするためのファーマットです。具体的には、「http://~」あるいは「https://~」で始まるURL(Uniform Resource Locator)/URI(Uniform Resource Identifier)という形式で送られます。
皆さんがブラウザにURLを入れたり、リンクをクリックしているときに、自動的にブラウザがHTTPのフォーマットに従ったリクエストを作成して、サーバにリクエストしています。

コマンドでホストに接続してみよう

ターミナルから「TECH CAMPのホームページ」に接続することを例としてブラウザが裏で処理しているか、コマンドを使って手動実行してみましょう。ホストに接続するには、telnetコマンドを使用します。

telnetコマンド

telnetコマンドは、離れたところにあるホスト(サーバやネットワーク機器)を手元の端末から遠隔操作するために、ネットワーク接続するコマンドです。

作業手順

①URLバーに「https://tech-camp.in」と入力する
②TECH CAMPのTOPページが表示される

手順

①digを使ってhttps://tech-camp.in のIPアドレスを調べる
②telnet でリクエストしてみる

dig tech-camp.in +noedns

以下の部分を確認しましょう!

;; ANSWER SECTION:
tech-camp.in.       11  IN  A   13.230.191.243 

たくさん情報が出てくると思いますが、「ANSWER SECTION」だけに注目してください。(実際のIPアドレスは異なることがあります。)

次にtelnetを使うのですが、ブラウザで Webページを開く際、ブラウザはサーバに下記のような要求メッセージを送信します。

$ telnet <dig tech-camp.in +noednsで表示されたIPアドレス> 80
# 例えばIPアドレスが「13.230.191.243 」であれば、「telnet 13.230.191.243  80」となる
brew install telnet

telnetを実行すると

Trying 13.230.191.243...
Connected to ec2-13-230-191-243.ap-northeast-1.compute.amazonaws.com.
Escape character is '^]'

上記のような結果が返ってきます。トップページが取得できました。

最後に以下のようにGET / HTTP/1.0を追記し,ENTERを2回押してください。

Trying 13.230.191.243...
Connected to ec2-13-230-191-243.ap-northeast-1.compute.amazonaws.com.
Escape character is '^]'.
GET / HTTP/1.0

実行すると、ターミナルにトップページのHTMLが返ってくると思います。

HTTP/1.1 200 OK
Date: Wed, 08 Jan 2020 02:27:38 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 29951
Connection: close
Server: nginx/1.10.1
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
ETag: W/"faa286c66f8e354025a495a0fe7282a7"
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: _tech-camp%E3%80%80-d_session=S3FUMkREYkFLcXB4OTBOb01NS1pPa2pEb3duWVlUM2tCZ0U3Sys5QU45UXdTQXVnL2lhcmFCdHdSSGw3TkFxVTJRWXJZV0FpK2xTdHZlditwNHhhUk01SU12YzZwWk9Md1lqbU9YeDRKM1NvY09mSUlJVGRia3dEc0NVVjE2elVaVUxTRDdranU5MzZSdjNnY3NLUkVRPT0tLWJSS2V3empPMFowVUM0NzRNNUtiQlE9PQ%3D%3D--573b3947da1245e8d5bdba7702e497fc081fa692; path=/; HttpOnly
X-Request-Id: 58c478f2-48f5-4ab3-8443-5d4d44c8a2fa
X-Runtime: 0.012434
Vary: Origin
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff

<!DOCTYPE html>
・
・
中略
・
・

<!DOCTYPE html>より上の部分がhtmlの「レスポンスヘッド(headタグ)」の部分でそれ以降が「メッセージボディ(bodyタグ)」の部分に当たります。
中略の部分は「bodyタグ」になっているので、試して確認してください。
今回は最後に「レスポンスヘッド(headタグ)」について確認します。

レスポンスヘッドとは?

レスポンスヘッドとはHTMLで学習したheadタグと同じです。「メッセージボディ(bodyタグ)」で足りない情報をメタ情報として記述しています。
ここでいうメタ情報とは、「データに関する補足情報」と思ってください。次に上から順に詳しく見てみましょう。

HTTP/1.1 200 OK

レスポンスが成功した際に表示されるものです。

Content-Type

HTMLファイルに使用している文字コードを示しておき、文字化けや誤動作を回避するために書いてあります。

P3P

P3Pはサイト運営者が個人情報を保護しながら、ユーザーが自分の情報をどの程度Webサイトに提供するかをコントロールできるようにする技術仕様です。
例えば、cookie(WEBサーバーがユーザーを認識する仕組み)を使うとユーザーのアクセス情報を収集することができます。(それを通じて無駄なく広告を打つことが可能です。)
しかし、プライバシーの観点からそれを保護する動きが高まりました。
それをP3Pを導入することによってユーザーの必要ない情報を受け取れないようにしています。

X-Content-Type-Options: nosniff,X-XSS-Protection: 1; mode=block,X-Frame-Options: SAMEORIGIN,X-XRDS-Location: ,

XXS(クロスサイトスプリング)によってCookie等の情報が盗まれないようにするためのものです。

Vary: Accept-Encoding,Expires: -1,Pragma: no-cache,Cache-Control:

こちらはキャッシュを残すためのものです。
キャッシュとは、使用頻度が高いデータを複製し記憶装置に蓄えられるものです。キャッシュを利用することによってより早いページの更新を行うことができます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

商品一覧ページ内のカートに入れる機能の実装(非同期通信)

はじめに

商品一覧ページ内の各商品に割り振られているカートに入れるボタンが押された際にuser_idとeffector_id(現在作成中のアプリケーションではエッフェクターが商品のため各所でeffectorを使用していく)にログイン中のuserのidとクリックされた商品のidを保存することを目指す。今回は非同期通信を使用したい。

テーブル、コントローラー、APIの作成

cartsテーブルの作成

ターミナル
アプリケーションのディレクトリ$ rails g model cart

cartモデルの作成

(migrateファイル)create_carts.rb
class CreateCarts < ActiveRecord::Migration[5.0]
  def change
    create_table :carts do |t|
      t.references :user, foreign_key: true
      t.references :effector, foreign_key: true
      t.timestamps
    end
  end
end

マイグレーションファイルの編集

ターミナル
アプリケーションのディレクトリ$ rails db:migrate

マイグレーションの実行

(modelファイル)cart.rb
class Cart < ApplicationRecord
  belongs_to :effector
  belongs_to :user
end
(modelファイル)effector.rb
class Effector < ApplicationRecord
  belongs_to :genre
  has_many :users, through: :carts
  #今回はcartsコントローラーのshowアクションを使用するため上の記述は不要
  has_many :carts
end
(modelファイル)user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :boards
  has_many :effectors, through: :carts
  #今回はcartsコントローラーのshowアクションを使用するため上の記述は不要
  has_many :carts
end

各モデル内にリレーションを記載

APIの準備

app/controller/api/carts_controller.rbを作成

app/controller/api/carts_controller.rb
class Api::CartsController < ApplicationController
  def create
    Cart.create(user_id: params[:user_id], effector_id: params[:effector_id])
    #json形式で送られてきたデータからparams[:user_id]をuser_idカラムにparams[:effector_id]をeffector_idカラムに保存
  end
end

ルーティング設定

routes.rb
Rails.application.routes.draw do
  root to: "effectors#index"
  devise_for :users
  resources :users, only: [:show, :edit, :update]
  resources :boards
  resources :effectors do
    resources :genres
  end
  resources :carts, only: [:show, :destroy]
  namespace :api do
    resources :carts, only: :create, defaults: { format: 'json'}
  end
  #cartsコントローラーとapp/controller/api/carts_controller.rbのルーティング設定
end

ここまで設定が終わったら商品一覧のカートに入れるボタンが押された際にcartsテーブルにuser_idとeffector_idが保存されるようにjsを記述していく。

商品一覧のカートに入れるボタンが押された時の処理

カートに入れるボタンが押された時onclick:を用いてjs内の関数を発火できるように記述する。

app/views/effectors/index.html.haml
.main
  .main__list
    - @effectors.each do |effector|
      .main__list__effector{data: {genre: effector.genre.id}}
        .main__list__effector__info
          .main__list__effector__info__visual
            %h.main__list__effector__info__visual__name
              = effector.name
            .main__list__effector__info__visual__image
              %img{alt: "image1", class: "main__list__effector__info__visual__image__file", src: "#{- effector.image1}"}
              %img{alt: "image2", class: "main__list__effector__info__visual__image__file", src: "#{- effector.image2}"}
          .main__list__effector__info__text
            %h.main__list__effector__info__text__genre
              = effector.genre.genre
            %h.main__list__effector__info__text__point
              = effector.point
              pt
            %br 
            %h.main__list__effector__info__text__detail
              = effector.text
          .main__list__effector__info__btns
            - if effector.youtube != nil
              .main__list__effector__info__btns__video
                %button.main__list__effector__info__btns__video__btn{onclick: "test(#{effector.youtube})"}
                  動画を視聴
            - if effector.link != nil
              .main__list__effector__info__btns__official
                = link_to "#{effector.link}", class: "main__list__effector__info__btns__official__btn" do
                  .main__list__effector__info__btns__official__btn__text
                    公式サイト
            - if user_signed_in?
              .main__list__effector__info__btns__cart
                %button.main__list__effector__info__btns__cart__btn{onclick: "createCart(#{current_user.id},#{effector.id})"}
                  -#クリックされた時関数名createCartを引数(current_user.id,effector.id)とし発火
                  カートに入れる
  .popup
    .popup__content
      .popup__content__youtube
        %iframe(width="560" height="315" id = "youtube_test" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen)
      %button#close
        閉じる

発火される関数の記述

effectors.js
function createCart(userId, effectorId) {
  var param = {
    user_id: userId,
    effector_id: effectorId
    //受け取った引数をuser_id:とeffector_id:として変数paramに格納
  }
  ajaxRequest("api/carts",'post',param)
  //非同期通信する際の関数を複数回の利用を見越して外出しして呼び出す(引数をapi/carts,post,paramとし呼び出し)
}

function ajaxRequest(url,type,data) {
  $.ajax({
    url: url,
    //url: api/cartsと同義
    type: type,
    //type: postと同義
    dataType: 'json',
    data: data
    //data: paramと同義
    //引数で持ってきた仮引数url,type,dataを使用しajaxの記述をする
  })
  .done(function() {
    alert('商品をカートに入れました')
  })
  .fail(function() {
    alert('error');
  });
}

ここまできたらjson形式で送られたデータをapi側のコントローラーで保存してあげるだけである。
先ほど記述したapp/controller/api/carts_controller.rbを見てみる

app/controller/api/carts_controller.rb
class Api::CartsController < ApplicationController
  def create
    Cart.create(user_id: params[:user_id], effector_id: params[:effector_id])
    #json形式で送られてきたデータからparams[:user_id]をuser_idカラムにparams[:effector_id]をeffector_idカラムに保存
  end
end

すでに記載している通りにjosn形式で送られてきたデータからcartsテーブルのuser_idカラムにparams[:user_id]をeffector_idカラムにparams[:effector_id]を保存する記載をする。
これにてカートに入れるボタンの実装が完了します。

終わりに

今回はカートに入れるボタンというショッピングサイトには欠かせない機能を非同期通信にて行えるように実装した。
ユーザーと商品という多対多という関係性、非同期通信でのデータの保存など初学者が苦労する機能の実装を行うことで力がつくのを感じました。
この機能はいいね機能などにも流用できると思うので学習する価値ありだと感じます。
2回目の投稿のため拙い文章が眼に余るかと思いますがご精読ありがとうございました。駆け出しエンジニアのみなさん一緒に頑張っていきましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

商品一覧ページ内のカートに入れるボタンの実装(非同期通信)

はじめに

商品一覧ページ内の各商品に割り振られているカートに入れるボタンが押された際にuser_idとeffector_id(現在作成中のアプリケーションではエッフェクターが商品のため各所でeffectorを使用していく)にログイン中のuserのidとクリックされた商品のidを保存することを目指す。今回は非同期通信を使用したい。

テーブル、コントローラー、APIの作成

cartsテーブルの作成

ターミナル
アプリケーションのディレクトリ$ rails g model cart

cartモデルの作成

(migrateファイル)create_carts.rb
class CreateCarts < ActiveRecord::Migration[5.0]
  def change
    create_table :carts do |t|
      t.references :user, foreign_key: true
      t.references :effector, foreign_key: true
      t.timestamps
    end
  end
end

マイグレーションファイルの編集

ターミナル
アプリケーションのディレクトリ$ rails db:migrate

マイグレーションの実行

(modelファイル)cart.rb
class Cart < ApplicationRecord
  belongs_to :effector
  belongs_to :user
end
(modelファイル)effector.rb
class Effector < ApplicationRecord
  belongs_to :genre
  has_many :users, through: :carts
  #今回はcartsコントローラーのshowアクションを使用するため上の記述は不要
  has_many :carts
end
(modelファイル)user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :boards
  has_many :effectors, through: :carts
  #今回はcartsコントローラーのshowアクションを使用するため上の記述は不要
  has_many :carts
end

各モデル内にリレーションを記載

APIの準備

app/controller/api/carts_controller.rbを作成

app/controller/api/carts_controller.rb
class Api::CartsController < ApplicationController
  def create
    Cart.create(user_id: params[:user_id], effector_id: params[:effector_id])
    #json形式で送られてきたデータからparams[:user_id]をuser_idカラムにparams[:effector_id]をeffector_idカラムに保存
  end
end

ルーティング設定

routes.rb
Rails.application.routes.draw do
  root to: "effectors#index"
  devise_for :users
  resources :users, only: [:show, :edit, :update]
  resources :boards
  resources :effectors do
    resources :genres
  end
  resources :carts, only: [:show, :destroy]
  namespace :api do
    resources :carts, only: :create, defaults: { format: 'json'}
  end
  #cartsコントローラーとapp/controller/api/carts_controller.rbのルーティング設定
end

ここまで設定が終わったら商品一覧のカートに入れるボタンが押された際にcartsテーブルにuser_idとeffector_idが保存されるようにjsを記述していく。

商品一覧のカートに入れるボタンが押された時の処理

カートに入れるボタンが押された時onclick:を用いてjs内の関数を発火できるように記述する。

app/views/effectors/index.html.haml
.main
  .main__list
    - @effectors.each do |effector|
      .main__list__effector{data: {genre: effector.genre.id}}
        .main__list__effector__info
          .main__list__effector__info__visual
            %h.main__list__effector__info__visual__name
              = effector.name
            .main__list__effector__info__visual__image
              %img{alt: "image1", class: "main__list__effector__info__visual__image__file", src: "#{- effector.image1}"}
              %img{alt: "image2", class: "main__list__effector__info__visual__image__file", src: "#{- effector.image2}"}
          .main__list__effector__info__text
            %h.main__list__effector__info__text__genre
              = effector.genre.genre
            %h.main__list__effector__info__text__point
              = effector.point
              pt
            %br 
            %h.main__list__effector__info__text__detail
              = effector.text
          .main__list__effector__info__btns
            - if effector.youtube != nil
              .main__list__effector__info__btns__video
                %button.main__list__effector__info__btns__video__btn{onclick: "test(#{effector.youtube})"}
                  動画を視聴
            - if effector.link != nil
              .main__list__effector__info__btns__official
                = link_to "#{effector.link}", class: "main__list__effector__info__btns__official__btn" do
                  .main__list__effector__info__btns__official__btn__text
                    公式サイト
            - if user_signed_in?
              .main__list__effector__info__btns__cart
                %button.main__list__effector__info__btns__cart__btn{onclick: "createCart(#{current_user.id},#{effector.id})"}
                  -#クリックされた時関数名createCartを引数(current_user.id,effector.id)とし発火
                  カートに入れる
  .popup
    .popup__content
      .popup__content__youtube
        %iframe(width="560" height="315" id = "youtube_test" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen)
      %button#close
        閉じる

発火される関数の記述

effectors.js
function createCart(userId, effectorId) {
  var param = {
    user_id: userId,
    effector_id: effectorId
    //受け取った引数をキーuser_id:とキーeffector_id:にそれぞれのバリューとして変数paramに格納
  }
  ajaxRequest("api/carts",'post',param)
  //非同期通信する際の関数を複数回の利用を見越して外出しして呼び出す(引数をapi/carts,post,paramとし呼び出し)
}

function ajaxRequest(url,type,data) {
  $.ajax({
    url: url,
    //url: api/cartsと同義
    type: type,
    //type: postと同義
    dataType: 'json',
    data: data
    //data: paramと同義
    //引数で持ってきた仮引数url,type,dataを使用しajaxの記述をする
  })
  .done(function() {
    alert('商品をカートに入れました')
  })
  .fail(function() {
    alert('error');
  });
}

ここまできたらjson形式で送られたデータをapi側のコントローラーで保存してあげるだけである。
先ほど記述したapp/controller/api/carts_controller.rbを見てみる

app/controller/api/carts_controller.rb
class Api::CartsController < ApplicationController
  def create
    Cart.create(user_id: params[:user_id], effector_id: params[:effector_id])
    #json形式で送られてきたデータからparams[:user_id]をuser_idカラムにparams[:effector_id]をeffector_idカラムに保存
  end
end

すでに記載している通りにjosn形式で送られてきたデータからcartsテーブルのuser_idカラムにparams[:user_id]をeffector_idカラムにparams[:effector_id]を保存する記載をする。
これにてカートに入れるボタンの実装が完了します。

終わりに

今回はカートに入れるボタンというショッピングサイトには欠かせない機能を非同期通信にて行えるように実装した。
ユーザーと商品という多対多という関係性、非同期通信でのデータの保存など初学者が苦労する機能の実装を行うことで力がつくのを感じました。
この機能はいいね機能などにも流用できると思うので学習する価値ありだと感じます。
2回目の投稿のため拙い文章が眼に余るかと思いますがご精読ありがとうございました。駆け出しエンジニアのみなさん一緒に頑張っていきましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ページトップボタン

以下のようなページトップボタン(ページの右下当たりにあって、クリックすると一番上に戻ってくるやつ)を実装します。

image.png

まず、ボタンを置きたいhtmlに以下をコピペ

index.html.erb(など)
<p id="pagetop"><a href="#">↑ </a></p>

これがページトップの指令になります。

次に以下を該当cssファイルにコピペしましょう。

tweets.css
/*ページトップ設定*/
#pagetop {
  margin-right: 10px;
    clear: both;
    padding-bottom: 40px;
}
#pagetop a {
    color: #FFF;        /*文字色*/
    font-size: 20px;    /*文字サイズ*/
    background: grey;   /*背景色*/
    text-decoration: none;
    text-align: center;
    display: block;
    float: right;
    border-radius: 30px;    /*角丸のサイズ*/
    width: 50px;
    line-height: 50px;
}

良い感じになるかと思います!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Web系のタグ

初めてのWeb系なので自分向けの備忘録

【input typeでのidとnameの違いと使い方】
id:CSSとかで指定する際に使う
name:値を渡すときに使う。

サーブレット側で受け取るときは「HttpServletRequest」のgetParameterメソッドにnameタグで指定した値を記載する。

【idなど画面に見せずにサーブレットに渡すとき】
input type="hidden"

【textareaに空白が入っちゃう】
textarea・・・
/textarea
として改行入れちゃってる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む