20200323のHTMLに関する記事は9件です。

Railsにて検索機能を実装 (全体一致や部分一致などのselectボックスを入れ、複数モデルからの検索を可能にする)

備忘録として、なるべく自分の理解している範囲で、考え方の順序なども記載していく。

railsを触って、数週間なので、初心者の人のために

慣れている人や頭のいい人だと、そんなのわかってるよと言われることまで書いていきます。

間違っていたら教えてください。

devise でログイン機能は追加しており

UserモデルとBookモデルを作っています。

検索フォームをログインしている場合、全てのページに表示させたいので

app/views/layouts/application.html.erb のviewに記載していく。

<% if user_signed_in? %>

  <%= form_tag(search_path, method: :get) do %>

  <%= select_tag 'range' ,options_for_select([['---選択してください---', ''], ['User', '1'], ['Book', '2']]) %>

  <%= select_tag 'search', options_for_select([["前方一致","forward_match"], ["後方一致","backward_match"], ["完全一致","perfect_match"], ["部分一致","partial_match"]]) %>
  <%= text_field_tag (:word) %>
  <%= submit_tag "検索" %>

  <% end %>

<% end %>

全体を<%= form_tag(search_path, method: :get) do %>と<% end %>でくくって

searchアクションにparamsである'range', 'search',(:word)を飛ばす

<%= select_tag 'range' ,options_for_select([['---選択してください---', ''], ['User', '1'], ['Book', '2']]) %>

これの意味は['User', '1']この[]の中のUserを選んだ場合(ドロップダウンリスト)

’1’を'range'というparamsに入れて、コントローラに飛ばすために書いた。

<%= select_tag 'search', options_for_select([["前方一致","forward_match"], ["後方一致","backward_match"], ["完全一致","perfect_match"], ["部分一致","partial_match"]]) %>

これも同様にドロップダウンリストで選べるようにしているのですが

"前方一致"を選んだら"forward_match"を'search'というparamsに入れてsearchコントローラに飛ばすために書いた。

この2つのコードはselectボックスを作るために

select_tag 'paramsを決めてここに書く', options_for_select

params名はなんでも良い。

<%= text_field_tag (:word) %>

これは検索フォームに入力したワードをwordというparamsに入れて上と同様にsearchコントローラに飛ばすために書いた。

つまり3つのparamsをsearchコントローラに飛ばすコードを書いた。

2020-03-23 18.24のイメージ.jpg

こんな感じのができる。

続いてsearchesコントローラを書いていく

 
class SearchesController < ApplicationController

  def search
    @range = params[:range]
    search = params[:search]
    word = params[:word]

    if @range == '1'

    @user = User.search(search,word)

    else
    @book = Book.search(search,word)
  
    end

   end

viewページから飛ばした3つのparamsをそれぞれインスタンス変数かローカル変数の中に入れるために
@range = params[:range]
search = params[:search]
 word = params[:word]

を書いた。

searchとwordをローカル変数にしたのはmodelのメソッド(今回だとUserモデルとBookモデル)で利用するため、インスタンス変数じゃなくていいから。

    if @range == '1'

    @user = User.search(search,word)

    else
    @book = Book.search(search,word)

1をユーザーが選んだ場合、@userをviewページで反映させ、2を選んだら@bookを反映させる条件分岐。

ここで.search(search,word)という searchメソッドを定義しなければいけない。

これはモデルで定義していく。

モデルについて

以下がモデルに検索のメソッドを定義する部分です。

ドロップダウンリストでUserを選んだ場合、

Userモデルにsearchメソッドを書いて、それをsearchコントローラーで呼べるようにする。

app/models/user.rb

def self.search(search,word)
  if search == "forward_match"
   @user = User.where("name LIKE?","#{word}%")
  elsif search == "backward_match"
   @user = User.where("name LIKE?","%#{word}")
  elsif search == "perfect_match"
   @user = User.where("#{word}")
  elsif search == "partial_match"
   @user = User.where("name LIKE?","%#{word}%")
  else
   @user = User.all
end
end

この書き方はググって出てきたものを真似した。

注意点は自分で定義した(search,word)2つのparamsを書くこと

searchにはviewページに書いた"forward_match"や"backward_match"が入ってきている。

wordにはユーザーが検索フォームに入れたワードが入っている。

User.where("name LIKE?","#{word}%")

このコードはUserモデルから検索ワードにヒットしているかを確認するコードで

nameはUserテーブルのカラム名を記載する。

名前での検索だと思うのでnameにしてある

続いて、ユーザーBookを選んだ場合のメソッドをBookモデルに書いていく

def self.search(search, word)
    if search == "forward_match"
                    @book = Book.where("title LIKE?","#{word}%")
    elsif search == "backward_match"
                    @book = Book.where("title LIKE?","%#{word}")
    elsif search == "perfect_match"
                    @book = Book.where("#{word}")
    elsif search == "partial_match"
                    @book = Book.where("title LIKE?","%#{word}%")
    else
                    @book = Book.all
    end

end

Userモデルと同様の書き方にする

そして検索結果を表示するviewページを作成

今回はsearchesフォルダにsearch.index.html.erbを作成したので

routes.rbに

get 'search' => 'searches#search'

追記した。

続いて

search.index.html.erbには

<h2>Results index</h2>
    <!--books一覧 -->
    <table class="table table-hover table-inverse">
        <thead>
            <tr>
                <th></th>
                <th>Title</th>
                <th>Opinion</th>
                <th colspan="3"></th>
            </tr>
        </thead>

  <tbody>
    <% if @range == '2' %>
            <% @book.each do |book| %>
            <tr>
                <td>
                    <%= link_to(book.user) do %>
                    <%= attachment_image_tag(book.user, :profile_image, :fill, 50, 50, fallback: "no-image-mini.jpg") %>
                    <% end %>
                </td>
                <td><%= link_to book.title, book, class: "book_#{book.id}" %></td>
                <td><%= book.body %></td>
      </tr>
      <% end %>
  </tbody>
    <% else %>
      <thead>
        <tr>
          <th>image</th>
          <th>name</th>
          <th colspan="3"></th>
        </tr>
      </thead>

      <tbody>
        <% @user.each do |user| %>
        <tr>
          <td><%= attachment_image_tag(user, :profile_image, :fill, 50, 50, fallback: "no-image-mini.jpg") %></td>
          <td><%= user.name%></td>
          <td><%= link_to "Show", user, class: "user_#{user.id}" %></td>
        </tr>
        <% end %>
      </tbody>
         <% end %>
    </table>

終わりに
以上が検索機能の実装方法になります。
疑問、気になるところがございましたら、質問、コメントよろしくお願いします!!!

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

table系タグは2種類にグループ分けすると覚えやすい

table系タグは2種類にグループ分けすると覚えやすい

(1)エリア系タグ...表自体のエリアごとの意味を表す

  • <table>...「このエリアは表」
  • <thead>...「このエリアは表の見出し」
  • <tfoot>...「このエリアは表のフッター」
  • <tbody>...「このエリアは表のメイン」
  • <tr>...「ここからここまでが1行」(<th><td>を囲います)

(2)コンテンツ系タグ...表の中身を表す

  • <th>...「これが見出しの文字」
  • <td>...「これが見出し以外(データ等)の文字」
example
<table> <!-- 開始:表エリア -->
 <thead> <!-- 開始:見出しエリア -->

   <tr> 
     <th>見出しA</th> <!-- コンテンツ系タグ:見出し -->
     <th>見出しB</th>
   </tr>

 </thead> <!-- 終了:見出しエリア -->
 <tbody> <!-- 開始:メインエリア -->

   <tr>
     <td>AA</td> <!-- コンテンツ系タグ:見出し以外(データ等) -->
     <td>BB</td>
   </tr>
   <tr> <!-- 開始:1行 -->
     <td>AAA</td>
     <td>BBB</td>
   </tr> <!-- 終了:1行 -->
   <tr>
     <td>AaAa</td>
     <td>BbBb</td>
   </tr>

 </tbody> <!-- 終了:メインエリア -->
</table> <!-- 終了:表エリア -->

この記事を作った経緯:

テーブルを作るとき、よく基本構造やそれぞれのタグの意味を忘れてしまうので自分なりの覚え方を考えました。その備忘録がこの記事です。

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

HTML,CSSでのwebサイト制作の手順とポイント

はじめに

本記事は

「progateやドットインストールなどでhtml、cssの文法的なインプットはしたが、いざwebサイトを作成する場合にどの手順を踏めば良いのか??」

という人を初学者を対象にhtml、cssを使ってwebサイトを作る際の手順と、その際に念頭におくべき事をまとめたものです。

拙い内容だとは思いますが、参考になれば幸いです!

※本記事はデザインが既にできている場合の手順になります。
①~③の過程は実際にデザインツールか紙を使って書く事を強くお勧めします!!

① デザインの全ての要素を隙間のない□で埋める!

ポイント

・全てを□で隙間なく埋め切る事が重要!

例 赤線にて区切り

スクリーンショット 2020-03-23 12.13.56.png

スクリーンショット 2020-03-23 12.18.59.png

② ①で区切った□をさらに1つ1つを細かいパーツに分ける

②-1 まずは大きなパーツから分けていく(青線)

スクリーンショット 2020-03-23 12.27.01.png

②-2 大きなパーツの中を更に細分化できる所まで分けていく(緑線)

スクリーンショット 2020-03-23 12.34.38.png

ポイント

・大きな□の中に小さな□が敷き詰められている状態になる
・この□の1つ1つがdiv要素であると考える!!(正確にはdiv以外のタグも使うが、、)

③ □の1つ1つに名前(class名)を付ける!!

③-1 赤線で囲った□に名前をつける

スクリーンショット 2020-03-23 12.58.25.png

③-2 青線で区切った□に名前をつける

スクリーンショット 2020-03-23 13.02.44.png

③-3 緑で区切った□に名前をつける

スクリーンショット 2020-03-23 13.07.09.png

ポイント

・大きい部分から名前をつけていく(赤線→青線→緑線)
・名前は基本的に重複なく付ける
・全く同じパーツを複数回使うときは、同じ名前をつける
・名前の付け方(命名規則)はプロジェクト単位で行う→1人でのプロジェクトでないなら、名前を付けた段階でレビューを入れると良い!!

参考用:代表的な命名規則について

https://webdesign-trends.net/entry/9214

④ htmlで□をネスト構造(入れ子構造)で書いていく

例:上記のbody1部分

<div class="body1">//赤線
    <div class="contents">//青線
      //緑線 <div class="img"><img src="" alt=""></div>緑線//
      //緑線 <div class="text"><p>説明文説明文</p></div>緑線//
     </div>青線//
    <div class="contents">//青線
      //緑線 <div class="img"><img src="" alt=""></div>緑線//
      //緑線 <div class="text"><p>説明文説明文</p></div>緑線//
     </div>青線//
    <div class="contents">//青線
      //緑線 <div class="img"><img src="" alt=""></div>緑線//
      //緑線 <div class="text"><p>説明文説明文</p></div>緑線//
     </div>青線//
</div>//赤線

セマンティクスwebについて

セマンティクスwebはモダンなhtmlの書き方として、html5から導入された「より要素の内容に沿ったhtmlタグ使おうとする考え方」です!!
構造の綺麗なhtmlを書く事ができ、SEOなどの対策になります!!

先ほどのhtmlは分かりやすさ重視の為に、全ての要素をdivタグで囲いましたが、セマンティクスwebを導入するとdivの代わりに様々なhtmlタグを使用していく事になります。

<main class="body1">//赤線
    <article class="contents">//青線
      //緑線 <div class="img"><img src="" alt=""></div>緑線//
      //緑線 <div class="text"><p>説明文説明文</p></div>緑線//
     </article>青線//
    <article class="contents">//青線
      //緑線 <div class="img"><img src="" alt=""></div>緑線//
      //緑線 <div class="text"><p>説明文説明文</p></div>緑線//
     </article>青線//
    <article class="contents">//青線
      //緑線 <div class="img"><img src="" alt=""></div>緑線//
      //緑線 <div class="text"><p>説明文説明文</p></div>緑線//
     </article>青線//
</main>//赤線

htmlを書く際はセマンティクスwebを意識してhtmlタグを工夫すると良いでしょう!!

参考用:セマンティクスwebについての詳細

https://developer.mozilla.org/ja/docs/Glossary/Semantics

ポイント

・「赤線の要素→青線の要素→緑線の要素」が入れ子になるようにhtmlを書く
・htmlをまず書き出して、必要としている要素が全て出力されているかブラウザで確かめる

⑤ cssにてデザインを整える

htmlでブラウザ上に各要素は出力されているので、cssでデザインに沿ってよしなにスタイリングして下さい。

まとめ

この手順は、「とにかくブラウザ上で成果物が目で見える状態にする。その上で細かいスタイリングを行なっていく!」という考え方に基いたものです。

こうする事で第三者に対して、早い段階から成果物の進捗を共有しやすくなったり、cssなどの挙動をブラウザでいちいち確認できるといった恩恵が受けられます。

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

Cordovaのチュートリアルをやってみる

公式チュートリアル:http://ccoenraets.github.io/cordova-tutorial/index.html

Cordovaプロシェクトの作成

Node.jsの最新バージョンをインストール

Cordova CLIのインストール

[windows]
npm install -g cordova

すでにインストール済みの場合は最新バージョンにアップデート
npm update -g cordova

[mac]
sudo npm install -g cordova

すでにインストール済みの場合は最新バージョンにアップデート
sudo npm update -g cordova

プロジェクトを保存するディレクトリに移動し、workshopという名前のディレクトリにWorkshopというCordovaプロジェクトを作成。

cordova create workshop com.yourname.workshop Workshop

プロジェクトディレクトリに移動。

cd workshop

SDKのインストール
workshopディレクトリにて
[iOS]

cordova platforms add ios

[Android]
https://developer.android.com/studio からインストール後

cordova platforms add android

workshopディレクトリにて、基本的なプラグインを追加

cordova plugin add org.apache.cordova.device
cordova plugin add org.apache.cordova.console

上記は旧式らしくエラー出る。

cordova plugin add cordova-plugin-splashscreen
cordova plugin add https://github.com/apache/cordova-plugin-console.git#1.0.0

上記で対応可?
詳細は未調査なので後ほど探ります。

エラー対応の参考:https://qiita.com/konyavic/items/ca6bec224ddd6abeb0ac

ディレクトリ構成

wwwが、コーディングファイル保存場所

platformsは、iOS/Androidなど異なるプラットフォームのためのアプリケーションをビルドする場所。
いじらない。

プラグインはpluginsにインストールされる。

アプリケーションパラメータ(名前、作成者など)はconfig.xmlに保存される。

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

UIkit3の拡張用CSSファイル(UIkit Plus α)を公開してみた

最近Web開発をする際のフロント側のCSSフレームワークに、UIkit3 をよく使用しています。(特に指定が無い限り)
個人的にBootstrapMaterializeなどいろいろと使ってきましたが、自分的にこれが一番使いやすかったので。(あとデザインが好き)

ただしUIkitのみで全てを賄える訳ではもちろんないので、必要に応じて独自のCSS定義を追加するのですが、そういった時には決まって、

  • そういえば以前にも同じ定義を書いたなようなと思いつつ同じ定義をまた書く
  • 以前作ったものを探してきてコピーして使用する

ということを繰り返してました。

そんな経緯から、なら 良く使うものをまとめてしまえ! と思い至り、
せっかくまとめたのなら、どれだけ需要があるのか分かりませんが、 公開してしまえ! とw
そしてUIkitがもっと普及するといいな。

というわけで公開したのが UIkit Plus α になります。
UIkit3の使用が前提となりますが、ご自由にお使いいただければと。

余談ですが、
昔は自分にもデザインはデザイナーさんの仕事だよね?と思っていた時もありました。
ただ最近はこういったCSSフレームワークの普及などにより、ある程度なら簡単にデザインが組めるようになり、開発時の線引きも曖昧に。
さすがにある程度はデザインをいじれないとマズイかなと思い、手を出したところ、、、見事にハマってしまいました。(笑)
開発するうえで、機能的な要件を満たすことは当然ですが、見栄えというのも大事ですよね。

UIkit Plus α

UIkit3と合わせて取り込むことで、少し拡張(使用できるCOMPONENTSを追加)することができます。
日頃の開発の中で今後も使いそうだなと思ったものをCOMPONENT化しています。(独断と偏見によりw)
同じくUIkitを使用する方の手助けになれば幸いです。
まだまだCOMPONENTの数は少ないですが、今後も随時COMPONENTを追加していく予定です。

使い方や追加されるCOMPONENTSの詳細は下記ページをご覧ください。
実際のデザインやサンプルコードを確認頂けます。
Document

「こんなCSS定義をよく使うんですけど」などを
GitHubのIssueに書き込んでくれれば、もしかしたら(もしかしたらですよ?)追加されるかもしれません。

UIkit Plus α で追加されるCOMPONENTS

  • Paragraph - 段落表示
  • Sub Text - サブテキスト
  • Box List - ボックスタイプのリスト
  • Border Table - 枠線タイプのテーブル
  • Titled Preformatted Text - タイトル付き整形済みテキスト
  • Side Fix Nav - 右上固定のナビゲーション
  • Floating Button - フローティングボタン
  • Margin Mini - 極小のマージン設定
  • Message Box - メッセージ表示
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML:フォームを作ろう

フォームについて

今回はフォームを作ってきます:relaxed:
フォームとは、ユーザーがテキスト入力したり、選択したりできるものです。
お問い合わせや、検索ボックス、会員登録などの際にWebページでよく見かけるのではないでしょうか。

お問い合わせフォームの例を見ていきましょう:point_down_tone2:
image.png

このフォームは実は多くのパーツを組み合わせて作られているのです。
そのパーツを1つ1つ勉強していきましょう:bangbang:

今回の記事:arrow_down:

  • フォーム欄を作るformタグ
  • テキスト入力欄
  • ラジオボタンを作る
  • チェックボックスを作る
  • 送信ボタンの作り方
  • セレクトボックス
  • 複数行テキスト入力欄
  • より使いやすいフォームにするには

フォーム欄を作るformタグ

<form>タグとは、フォームを作成するためのタグで、フォームを作る際には<form>タグで使用するすべてのパーツを囲う必要があります。

記述する主な属性は以下の通りです。

属性 用途
action データ送信先ページを指定
method データの送信方法を指定。主にgetかpostを入力
name フォームの名前を指定
フォームタグの例
<form action="./Homeservlet" method="post" name="contact_form">
        <!-- ここにフォームの部品が入ります。 -->
</form>

この例だと、入力した内容がHomeservlet.javaに送られます。(JSP,Servlet使用の場合)
次からフォームの部品について見ていきます。
多くのパーツはinputタグで書いていきます。

テキスト入力欄

部品部分はinputタグのtype属性によって変わってきます。1行のテキスト入力の場合は、type属性の中にtextと書きます。

1行テキストの例
お名前:<input type="text">

すると入力欄に文字が入力できるようになります。
GIF 2020-03-20 12-24-45.gif

入力欄に最初からテキストを表示

入力欄に最初からテキストを表示することも可能です。
placeholder属性を使用します。

テキスト初めから表示
お名前:<input type="text" placeholder="名字 名前">

image.png

色々な種類の1行テキスト

1行テキストには入力する内容が多々あります。
type属性の中に様々なものを指定できます。この属性値をサポートしているブラウザで正しい書式をチェックしてくれます。

属性値 用途
text 1行テキスト(初期値) 
search 検索するときのテキスト
email メールアドレス
tel 電話番号
url WebサイトのURL

ラジオボタンを作る

複数ある選択肢のうち、1つのみ選択してももらいたい時にはラジオボタンを使用します。
ユーザーが一つをクリックして選択すると、その他の選択肢は自動的に選択できなくなります。
ラジオボタンを作る場合は、type属性の中にradioと書きます。

主な属性

属性値 用途
name ラジオボタンの名前 
value 送信される選択肢の名前
checked 最初から選択されている状態にするとき指定
ラジオボタンの例
 性別:
    <input type="radio" name="gender" value="男"><input type="radio" name="gender" value="女" checked><input type="radio" name="gender" value="その他">その他

image.png

複数の選択肢があるラジオボタンでは、それぞれに同じname属性の値を付けることで1つのグループとしてまとめられます。checked属性を指定したラジオボタンは最初から選択状態になります。

チェックボックスを作る

チェックボックスはラジオボタンのように複数ある選択肢から選んでもらうパーツですが、ラジオボタンと違いチェックボックスの場合は複数の項目を選択可能です。
チェックボックスを作る場合は、type属性の中にcheckboxと書きます。

主な属性

属性値 用途
name チェックボックスの名前 
value 送信される選択肢の値
checked 最初から選択されている状態にするとき指定
チェックボタンの例
 好きな色:
   <input type="checkbox" name="color" value="赤" checked><input type="checkbox" name="color" value="青"><input type="checkbox" name="color" value="黄"><input type="checkbox" name="color" value="緑">

image.png

ラジオボタンと同じく、それぞれに同じname属性の値を付けることで1つのグループとしてまとめることができます。checked属性も同様です。
チェックボックスは、趣味や利用目的など、複数の答えが考えられるときに利用しましょう。

送信ボタンの作り方

フォームに入力した内容を送信するパーツです。
送信するパーツを作る場合は、type属性の中にsubmitと書きます。
ボタン上に表示させるテキストは「送信」でなくても大丈夫です。例えば検索フォームなら、「検索」、会員登録フォームなら「登録」と、用途に合わせて変えるといいでしょう。

主な属性

属性値 用途
name ボタンの名前 
value ボタンに表示されるテキスト
送信ボタンの例
<input type="submit" value="送信">

image.png

セレクトボックス

セレクトボックスをクリックすると、選択肢が表示されるパーツです。選択肢全体を<select>タグで囲み、選択項目はそれぞれ<option>タグで囲みます。

<select>タグの主な属性

属性値 用途
name セレクトボックスの名前 
multiple ShiftまたはCtrlキーで複数の項目を選択可能にする

<option>タグの主な属性

属性値 用途
value 送信される選択肢の値 
selected 最初から選択されている状態にするときに指定
セレクトボックスの例
    <select name="bloodtype">
        <option value="A">A</option>
        <option value="B">B</option>
        <option value="O">O</option>
        <option value="AB">AB</option>
        <option value="不明" selected>不明</option>
    </select>

GIF 2020-03-22 17-15-19.gif

複数行テキスト入力欄

<textarea> タグを使えば、複数行にわたるテキストを入力できます。お問い合わせ内容やメッセ時を入力するときに使われます。<textarea> タグで囲まれた部分が初期値として表示されます。

<textarea> タグの属性

属性値 用途
name 入力フィールドのの名前 
cols 一行当たりの最大文字数の目安を指定する
rows 表示行数を指定する
placeholder ユーザー入力に関するヒントを短めのテキストで指定
複数行テキスト入力の例
   <textarea name="message" cols="30" rows="10">メッセージを入力</textarea>

GIF 2020-03-23 7-45-39.gif

上記のように<textarea> タグで囲まれた部分は入力欄をクリックしても消えないので、 placeholder属性で指定するとよいでしょう。

複数行テキスト入力の例
<textarea name="message" placeholder="メッセージを入力" cols="30" rows="10"></textarea>

GIF 2020-03-23 7-51-47.gif

より使いやすいフォームにするには

フォームを設置する時、例えば1行テキスト入力欄横に「名前」「電話番号」などの文字表示させます。これらのテキストのことを「ラベル」と呼びます。ラベルをつけることでより使いやすいフォームになります。
ラベルには<label>タグを使います。ラジオボタンやチェックボックスをクリックする際などに使用します。

属性値 用途
for フォーム部品とラベルを関連付ける 

関連付けたいフォームにはid属性を付けます。このfor属性とid属性の値を同じものにしておけば関連付けられます。

ラベルの例
    <input type="checkbox" name="travel" value="日本国内" id="japan">
    <label for="japan">日本国内</label>

    <input type="checkbox" name="travel" value="ヨーロッパ" id="europe">
    <label for="europe">ヨーロッパ</label>

    <input type="checkbox" name="travel" value="東南アジア" id="asia">
    <label for="asia">東南アジア</label>

GIF 2020-03-23 8-06-59.gif

これで文字の部分をクリックしてもチェックがつくので、使いやすいですね:smiley:

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

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

100日チャレンジの270日目

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

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

モバイル画面でbackground-attachement: fixed; が効かない

モバイル画面でbackground-attachement: fixed; が効かない場合

ちょっとおしゃれなホームページを作ろうと思ったら、
簡単にできるものとして背景固定のパララックス風のページデザインがありますよね。

こんな感じのウェブサイト

その際は

hogehoge.css
body{
   background-image: url('hogehoge.jpg');
   background-attachment: fixed;
   background-size: cover;
}

とかで背景画像を固定するのが一般的かな?

が、実はこの
background-attachment: fixed;
iOSではうまく動かないんです。

正しくは、iOSモバイル画面で見てみると背景画面が固定されません、、、
background-attachment:fixed;とbackground-sizeはモバイルだと衝突するみたいです。

じゃあどうするの?ってことで
以下解決策

bodyに::before疑似要素で背景画像を指定!!

hogehoge.css
body{
   width:100%;
   z-index: 0;
}

body::before{
   content: "";
   display: block;
   position: fixed;
   top: 0;
   left: 0;
   z-index: -1;
   width: 100%;
   height: 100vh;
   background-image: url('hogehoge.jpg');
   background-size: cover;
}

bodyの擬似要素(z-index:-1;)をbody(z-index:0;)の一枚裏におけばおk

content: "";

 
→ 私のような初学者は『これ何?』とお思いだろうが、
::after とか ::before についてはcontentプロパティがないと要素として全く見えなくなります。
のでつけときましょう。

これで::beforeを使ったbackground-attachment: fixed;の画面を実装することができました。

モバイルでの背景固定パララックス風デザインを実装する際は是非ご参考に!

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

Stripe決済のDjangoでの実装

初めに

Stripeを使えば比較的簡単に決済機能を実装できます。この記事ではStripeが用意した決済フォームにページをリダイレクトさせることで、自サイトではクレジットカード情報を保持することなく支払い機能を自サイトに組み込むことができます。

基本はStripeのドキュメントGithub上のサンプルを基にして実装しています。また、Djangoで実装します。正直、試行錯誤しながら適当に作ったので実装に関して突っ込みどころ満載です。

最終的にはStripeのサンドボックスの劣化版が実装されます。また、以下の情報は2020年3月時点のものです。

環境設定

  • Python==3.8.1
  • Django==3.0.4
  • stripe==2.43.0
  • django-environ==0.4.5

Step0: 前準備

適当にディレクトリを作成する。

mkdir stripe-test
cd stripe-test

pipenvを使って仮想環境を構築します。

pipenv --python 3.8
pipenv install django stripe django-environ

pipenv shellで仮想環境を起動する。pipenvについてさらに詳しく知りたい場合はPipenvを使ったPython開発まとめを参照。

# shell起動
pipenv shell

Djangoプロジェクトを作成する。

django-admin startproject stripe_test .
django-admin startapp checkout

Stripeにアカウント登録

Stripeにアカウント登録する。アカウント作成やダッシュボードの見方についてはRailsのdeviseとStripe(とBootstrap)の組み合わせで、ただただユーザー登録させたユーザーに定期更新購読させるだけのappを作りました②に詳しい。

公開ビジネス情報を入力する。2020年3月現在だと以下のURL。

次に開発用の公開キーとシークレットキーを確認する。2020年3月現在だと以下のURL。

Django Setting

Djangoのsettingを以下のようにする。.envファイルを取り込むのにdjango-environを使用している。

stripe_test/stripe_test/settings.py
import os
import environ

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_NAME = os.path.basename(BASE_DIR)

# django-environから環境変数を読み込む
env = environ.Env()
env.read_env(os.path.join(BASE_DIR, '.env'))

### 省略 ###

INSTALLED_APPS = [
    ...,
    'stripe',#追加
    'checkout.apps.CheckoutConfig', # 自作 チェックアウト
]

### 省略 ###

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')], #デフォルトから変更
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

### 省略 ###
# Static files (CSS, JavaScript, Images)

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

STRIPE_SECRET_KEY = env('STRIPE_SECRET_KEY')
STRIPE_PUBLISHABLE_KEY = env('STRIPE_PUBLISHABLE_KEY')

.envには例えばこういう設定を行う。使用通貨がアメリカドル(usd)の場合はセント単位で計算するので1234と入力した場合は$12.34となる。

.env
# Stripe API keys
STRIPE_PUBLISHABLE_KEY=pk_12345
STRIPE_SECRET_KEY=sk_12345

# Checkout settings 
DOMAIN=http://localhost:8000/
BASE_PRICE=1099
CURRENCY=usd

Step1:サーバー側でチェックアウト・セッションを実装する

セッションの作成はサーバー側で行う。Python + Django でJSONの送受信を参考にして、以下のようにDjangoを使って、Checkout Sessionを作成する。

stripe_test/checkout/views.py
# -*- coding: utf-8 -*-
import logging
import os
import json

from django.shortcuts import render
from django.conf import settings
from django.views import generic

import stripe
from django.http.response import JsonResponse
from django.views.decorators.csrf import csrf_exempt

logger = logging.getLogger(__name__)

@csrf_exempt
def onetime_payment_checkout(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        domain_url = os.getenv('DOMAIN')
        stripe.api_key = settings.STRIPE_SECRET_KEY
        try:
            # Create new Checkout Session for the order
            # ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
            checkout_session = stripe.checkout.Session.create(
                success_url=domain_url +
                "checkout/success?session_id={CHECKOUT_SESSION_ID}",
                cancel_url=domain_url + "checkout/canceled/",
                payment_method_types=["card"],
                line_items=[
                    {
                        "name": "Pasha photo",
                        "images": ["https://picsum.photos/300/300?random=4"],
                        "quantity": data['quantity'],
                        "currency": os.getenv('CURRENCY'),
                        "amount": os.getenv('BASE_PRICE'),
                    }
                ]
            )
            logger.debug( str(checkout_session))
            return JsonResponse({'sessionId': checkout_session['id']})
        except Exception as e:
            logger.warning( str(e) )
            return JsonResponse({'error':str(e)})

Step2:チェックアウトにリダイレクト

基本的にJavascriptを使って実装していく。実現していることはstripe.jsを使ってStripe側にデータを渡して、Stripeで決済してもらった後に自サイトに戻る。

stripe_test/templates/base.html
{% load i18n static %}
<!DOCTYPE html>{% get_current_language as LANGUAGE_CODE %}
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
        <title>{% block title %}{% endblock %}</title>
        <script src="{% static 'js/jquery-3.3.1.slim.min.js' %}"></script>
        <script src="{% static 'js/bootstrap.min.js' %}"></script>
        {% block additional_script %}
        {% endblock %}
    </head>
    <body>
        <div class="container">
            {% block content %}
            {% endblock %}
        </div>
    </body>
</html>
stripe_test/templates/checkout/checkout_test.html
{% extends "base.html" %}

{% load i18n static %}
{% block title %}Checkout_test{% endblock title %}

{% block additional_script %}
<script src="https://js.stripe.com/v3/"></script>
<script src="{% static 'checkout/js/script.js' %}"></script>
{% endblock additional_script%}


{% block content %}
<div>
    <h1 data-i18n="headline"></h1>
    <h4 data-i18n="subline"></h4>
    <div class="pasha-image">
      <img
        src="https://picsum.photos/280/320?random=4"
        width="140"
        height="160"
      />
    </div>
  </div>
  <div class="quantity-setter">
    <input type="number" id="quantity-input" min="1" max="2" value="1" />
  </div>
  <p class="sr-legal-text" data-i18n="sr-legal-text"></p>

  {% csrf_token %}
  <button
    id="submit"
  >PAYMENT</button>
</section>
<div id="error-message"></div>
</div>
{% endblock content %}

自作Scriptは以下のサイトを参考にした
- 参考Github
- Fetch を使う

stripe_test/static/checkout/js/script.js
/* Handle any errors returns from Checkout  */
var handleResult = function(result) {
    if (result.error) {
      var displayError = document.getElementById("error-message");
      displayError.textContent = result.error.message;
    }
  };

  // Create a Checkout Session with the selected quantity
  var createCheckoutSession = function() {
    var inputEl = document.getElementById("quantity-input");
    var quantity = parseInt(inputEl.value);
    return fetch("/checkout/create-checkout-session/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        quantity: quantity,
      })
    }).then(function(result) {
      return result.json();
    });
  };

  /* Get your Stripe publishable key to initialize Stripe.js */
  fetch("/checkout/config")
    .then(function(result) {
      return result.json();
    })
    .then(function(json) {
      window.config = json;
      var stripe = Stripe(config.publicKey);
      // Setup event handler to create a Checkout Session on submit
      document.querySelector("#submit").addEventListener("click", function(evt) {
        createCheckoutSession().then(function(data) {
          stripe
            .redirectToCheckout({
              sessionId: data.sessionId
            })
            .then(handleResult);
        });
      });
    });

HTMLとJavascriptが実装できたら、checkout_test.htmlが見られるようにviews.pyを以下を追加する。

stripe_test/checkout/views.py
class IndexView(generic.TemplateView):
    template_name = "checkout/checkout_test.html"
    def get(self, request, *args, **kwargs):
        return render(request, self.template_name )

とりあえずはcheckoutアプリのマッピング情報を記述して、最初のページだけ見られるようにする。これはcheckout/urls.pyを以下のように編集することで実現できる。

stripe_test/checkout/urls.py
from . import views
from django.urls import path,include

app_name = 'checkout'

urlpatterns = [
    path('',views.IndexView.as_view(), name="index"),
]

Djangoアプリではない、ホームページ側のurls.py(この記事ではstripe_test/stripe_test/urls.py)を以下のように修正する。

stripe_test/stripe_test/urls.py
from django.contrib import admin
from django.urls import path,include
from django.views.generic import RedirectView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', RedirectView.as_view(url='/checkout')),
    path('checkout/', include('checkout.urls')),
]

上記自作のjavascript(stripe_test/static/checkout/js/script.js)ではfetch("/checkout/config")を行っているが、これはjsonでstripeAPIの公開キーを受け取っている。サーバー側の実装は以下のとおり。

stripe_test/checkout/views.py
@csrf_exempt
def stripe_config(request):
    if request.method == 'GET':
        stripe_config = {
            'publicKey': settings.STRIPE_PUBLISHABLE_KEY,
            'basePrice': os.getenv('BASE_PRICE'),
            'currency': os.getenv('CURRENCY'),
        }
        return JsonResponse(stripe_config, safe=False)
stripe_test/checkout/urls.py
from . import views
from django.urls import path,include

urlpatterns = [
    ...,
    path('config/',views.stripe_config),
    ...,
]

python manage.py runserverでとりあえずサイトを起動してhttp://localhost:8000/にアクセスすると以下の感じの画面が出るはずだ。

サンプルページ.png

ただ、これだとPAYMENTを押しても、何も起きないはず。なぜなら、ボタン押されたあとの実装を行っていないから。具体的には自作javascriptのcreateCheckoutSession関数はとあるURLにPOSTしているが、Django側で設定していないので、設定を行う必要がある。それにはurls.pycreate-checkout-sessionを追加すればいい。

stripe_test/checkout/urls.py
urlpatterns = [
    ...,
    path('create-checkout-session/',views.onetime_payment_checkout , name='create-checkout'),
    ...,
]

以上まで実装を行えば、Stripe側にURLが飛んで決済情報を入力する画面が表示されるはずです(参考:Stripe Checkout)。後は、決済情報を入力し終わったリダイレクト先のページ(success.htmlcanceled.html)を実装していけば、一通り決済はできるようになっている。

テスト

テスト用の決済カードは複数用意されているので、適宜選ぶ。特にこだわりがないのならば 4242424242424242を使用。

もし、うまく実装できていればダッシュボード上に支払いが反映されているので、確認してみてください。

参考:Github

上記の実装はGithubに公開しているので、参考にしてください。
- Github上のサンプル

参考サイト

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