JavaScript/e.preventDefaultってなんやねん

って話。

結構JavaScriptを書いてると使う機会のあるコードなんだけど。

正直ちゃんと理解をして使ってるわけじゃない。

 

どうやら非同期通信を始めとした技術ではどうしても使わざるをえない大前提のような記述らしいのでちょっとまとめてみようかなっと。

 

preventDefaultとは

実行したイベントがキャンセル可能である場合、イベントをキャンセルするためのメソッドのこと。

ちなみにe.preventDefaultと使われることが多いと思うんだけど、eってのはeventのことらしい。

例えばリンクをクリックした時、チェックボックスにチェックを入れた時なんかにイベント処理が発火するはずなんだけど、これを適用することにより発火しなくなるということ。

 

いやいや.on('submit')とかで指定してるじゃんダメじゃん。

なんのためにやるんだよ動かなくなるじゃんと思ったんだけど。

だからこそちゃんと考えなくてはならないみたい。

つまり「prevent(妨害する)」 「Default(初期動作)」のうち、初期動作とはなんなのかってことを考える必要がある。

 

イベントの初期動作と仕様を考える

ChatSpaceを例に考えるなら。

◆初期動作

メッセージ送信機能はsubmit属性を持っているわけだけど、HTML上ではaタグと紐づいていて、設定されたリンク先に飛ぶことになる。

この設定されたリンク先に飛ぶっていう動作がこの場合の初期動作。

 

◆仕様

非同期通信がしたい。

つまり、ユーザーが送信ボタンを押した場合にブラウザをリロードせずにメッセージを反映させたいということ。

 

上記を踏まえて考えると、e.preventDefaultを使わないと、送信ボタンを押した場合にブラウザリロードされちゃうよねってこと。

 

まとめ

・e.preventDefaultはイベントの初期動作を発火させないようにするコード

・イベントごとの初期動作とは一体なんなのか?考える必要がある

・仕様を明確にする。

・初期動作と仕様を明確にした上で、e.preventDefaultを適用するかどうか決定する。

 

こんなとこっすかね

RubyOnRails/Carrierwaveとminimagickの導入

くそー変なところでつまづいてた〜〜〜。

 

ChatSpaceでずっとundefine method 'url'がでていて、変数として使用しているurlなんてmessage.image.url、つまりメッセージから取得した画像のurlしかなかったのです。

で、メッセージテーブルにurlカラムなんてないし、imageテーブルなんて作ってないし一体どういうことなんだと思っていたら。

 

Carrierwaveとminimagickを導入すればurlメソッドのようなものが使えて、メッセージから取得したimageファイルにurlを埋め込める・・らしい。

 

つまりCarrierwaveとminimagickを導入し忘れてたってこと!

今回はその導入方法を手早く書いていくぞ!

 

まずgemの記載。

gemfile

省略
gem 'mini_magick'
gem 'carrierwave'

 

忘れずにインストール!

ターミナル

bundle install

 

イメージアップローダーをジェネレートする

ターミナル

rails g uploader image

 

モデルに記述。

app/models/message.rb

省略
mount_uploader :image, ImageUploader

 

最後にイメージアップローダーに記述。

app/uploaders/image_uploader.rb

省略
include CarrierWave::MiniMagick
process resize_to_fit: [200, 200]
省略

include~~はimage_uploaderの4行目くらいでコメントアウトされてるので、コメントアウトを解除します。

process resize_to_fitは画像を投稿する際のサイズを決定しています。

 

RubyOnRailsでAjaxの実装

また備忘録的に書いていこうかなと。

 

今回はAjaxの実装について。

まずAjaxとはなんぞやと。

 

Ajax(Asynchronous JavaScript + XML)とは

ウェブブラウザ内で非同期通信を行いながらインターフェイスの構築を行うプログラミング手法である[4]XMLHttpRequestHTTP通信を行うためのJavaScript組み込みクラス)による非同期通信を利用し、通信結果に応じてダイナミックHTML (DHTML) で動的にページの一部を書き換えるというアプローチを取る[5]

(引用:https://ja.wikipedia.org/wiki/Ajax)

 

やっぱり文章にするとわかりづらいよね。

自分が調べていてイメージしやすいなと思ったのはグーグルマップかな。

ドラッグすると現在表示されている画面から、滑らかにスライドするように地図表示情報が見えてくると思ういます。

これが昔はドラッグして違う画面を見ようと思ったら、その度にページ読み込みをしていたので滑らかに見ることができなかったのです。

(自分はギリギリそんな過去があったなと思えるんだけど、知らない世代は知らないかも・・)

 

やっぱり使用感として使い心地が全然違うよねって話

 

今回はRailsでどうやって実装していくかについて簡単にまとめようかと思うよ。

ちなみに今回はツイートに対してコメントをした場合に、コメントが非同期通信で表示されるという機能をテーマに書いていきます。

 

1.まずはGemを導入。Gemfileに書いたら忘れずにbundle installを。

Gemfile

  ///
 (中略)
 
 
 (中略)
///
 

 

 

2.フォームが送信されたらイベントが発火するようにする

javascripts階層にcomment.jsを作成して、下記の通りコードを記載。

この時、フォームにID名(もしくはクラス名)をつけ忘れないように

app/assets/javascripts/comment.js

$(function(){
  $('#new_comment').on('submit', function(e){
    e.preventDefault();
    var formData = new FormData(this);
  })
})

 

3.非同期通信でコメントが保存されるようにする

上記コードに非同期通信をする上での詳しいオプションを書いていきます。

フォームの送信が行われた場合にcreateアクションを動かします。

この時のdata形式ではjsonを指定しています。

app/assets/javascripts/comment.js

$(function(){
  $('#new_comment').on('submit', function(e){
    e.preventDefault();
    var formData = new FormData(this);
    var url = $(this).attr('action')
    $.ajax({
      url: url,
      type: "POST",
      data: formData,
      dataType: 'json',
      processData: false,
      contentType: false
    })
  })

 

 

4.コメントを保存し、respond_toを使ってHTMLとJSONの場合で処理を分ける

responto_to do を使用し、リクエストされたformatによって処理を分けるようにしています。

フォーマットがjsonの場合、jbuilderを使ってJavaScriptに返すデータを作成します。

app/controllers/comments_controller.rb

class CommentsController < ApplicationController
 def create
   @comment = Comment.create(text: comment_params[:text], tweet_id: comment_params[:tweet_id], user_id: current_user.id)
   respond_to do |format|
format.html { redirect_to tweet_path(params[:tweet_id]) }
format.json
end
end

private
def comment_params
params.permit(:text , :tweet_id)
end
end

 

 

5.jbuilderを使用して、作成したメッセージをJSON形式で返す

jbuilderでは、[j.son.KEY VAKUE] という形で書くことができます。

こうすることにより、JavaScriptファイルに帰ってきたデータをjbuilderで定義したキーとバリューの形で呼び出して使うことができます。

app/views/comments/create.json.jbuilder

json.text @comment.text
json.user_id @comment.user.id
json.user_name @comment.user.nickname

 

 

6.帰ってきたJSONをdoneメソッドで受け取り、HTMLを作成する

function buildHTMLではHTMLを追加しています。

.done以降では非同期通信に成功した時の記述をしています。function(data)となっていますが、非同期通信に成功した時の第一引数にはサーバから返されたデータが入っています。

この場合ではcreate.json.jbuilderのデータです。

app/assets/javascripts/comment.js

$(function(){
 function buildHTML(comment){
   var html = `<p>
            <strong>
              <a href=/users/${comment.user_id}>${comment.user_name}</a>
              :
            </strong>
             ${comment.text}
          </p>`
    return html;
}
 
$('#new_comment').on('submit', function(e){
   e.preventDefault();
  var formData = new FormData(this);
  var href = window.location.href + '/comments'
   $.ajax({
     url: href,
     type: "POST",
     data: formData,
     dataType: 'json',
     processData: false,
     contentType: false
  })
 
   .done(function(data){
    var html = buildHTML(data);
    $('.comments').append(html)
    $('.textbox').val('')
    })
  })
});

 

7.エラー時の処理を行う

通信に失敗した場合、アラートを出します。

 

app/assets/javascripts/comment.js

(省略)
.done(function(data){
var html = buildHTML(data);
$('.comments').append(html)
$('.textbox').val('')

.fail(function(){
alert('error');
})
(省略)

 

大まかな流れはこんなところかな。

rails/deviseをrspecで使えるようにする準備

久々に書くよ。

最近はカリキュラムに追いつくのが必死でなかなかね・・。

 

あとはやっぱりがっつり書きすぎると続かないので、今回は本当に備忘録くらいの量で書こうかなと思う。

 

今回はRubyOnRailsでdeviseをrspecで使えるようにする準備手順について書くよ

 

まずは下記ファイルを作成、記載。

 

spec/support/controller_macros.rb

module ControllerMacros
def login(user)
@request.env["devise.mapping"] = Devise.mappings[:user]
sign_in user
end
end

 

spec/rails_helper.rb

RSpec.configure do |config|
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
config.include Devise::Test::ControllerHelpers, type: :controller
config.include ControllerMacros, type: :controller
#~省略~
end

 

これだけでdevise機能をrspec上で使用することができます。

今度はrspecの導入基礎も書きたいな。

 

 

 

 

JavaScript/繰り返し文

久々にブログを書く気になったので書こうと思う。

ブログのモチベーション保つの難しいよ〜。

夜まで勉強してるとどうしても書くのが深夜になっちゃうし・・・。

う〜ん、ちょっとやり方考えなきゃな。

 

とりあえず、今日はJavaScriptの繰り返し文について書こうかなと思う。

ここ一週間はTECH::EXPERTの中間試験、模試、本試験ばっかりでなかなか新しい知識がなかったんだけど、久々に。

 

ちょっと基礎を飛ばし気味になるけど、解説の中で説明するから許して。

 

ちなみにhtmlファイルの中の記述は下記で進めます。

htmlファイルの編集はしません。

 

ファイル名:blog.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JavaScript</title>
</head>
<body>
<script src="blog.js">
</script>
</body>
</html>

 

今回は以下のような仕様のものを作るよ。

JavaScript上で英数国理社5教科それぞれの点数を打ち込む。

・それを足し合わせた合計点をhtml上のコンソールに表示。

・合計点に応じてA、B、Cのランク付をする。

・合計点=500点⇨Aランク

・300<=合計点<500⇨Bランク

・合計点<300⇨Cランク

・ランクの結果もhtml上のコンソールに表示

ちなみに実行結果の様子と、コンソール画面は以下の画像みたいな感じになります。

htmlファイルをブラウザ上で開いて、F12を押すとコンソール画面に入れます。

f:id:AK474747:20190707021812p:plain

 

 

色々と長くなったので、いつものごとくコードをどん。

var subjects=[
{subject: "英語" , score: 50 },
{subject: "数学" , score: 50 },
{subject: "国語" , score: 50 },
{subject: "理科" , score: 50 },
{subject: "社会" , score: 50 },
]


function rank_judge() {
var total_score=0

for (var i = 0 ; i < subjects.length ; i++){
total_score = total_score + subjects[i].score
}

console.log(`合計点は${total_score}点です。`)

if (total_score === 500){
console.log("ランクはAです。")
}else if(total_score >= 300 && total_score < 500 ){
console.log("ランクはBです。")
}else{
console.log("ランクはCです。")
}
}

rank_judge()

 

順繰りに解説していきます。

var subjects=[
{subject: "英語" , score: 50 },
{subject: "数学" , score: 50 },
{subject: "国語" , score: 50 },
{subject: "理科" , score: 50 },
{subject: "社会" , score: 50 },
]

まずここ。

配列subjectsの中にハッシュがある状態です。

JavaScriptの場合は、変数を定める際にvarという単語を入れます。

あとはrubyと完全に同じ。

 

配列sabjectsは順番で値を管理します。

subjects[2]={subject: 国語, score: 50}となります。

このハッシュの中に入っている値を取り出す場合は、

subjects[2].scoreと記載します。この場合は国語の50点という値が返ってきます。

 

 

function rank_judge()

次にこの記述。

rubyでいうところのdefに当たります。

独自のメソッドを作れますよっていう便利構文です。

見切れちゃってますけど、メソッド全体を{ }で囲む必要があります。

( )内は引数です。今回は使いません。盛り込みたかった・・・・。

 

 

var total_score=0

for (var i = 0 ; i < subjects.length ; i++){
total_score = total_score + subjects[i].score
}

次にfor文。rubyではeach文がよく使われましたが、for文で記載しています。

var total_score=0は、教科の合計点数を出すために変数total_scoreを作成し、0点と定義しています。

 

3行目では、「変数iを0として定義し、配列の値の数までiを増やし続けろ」と命令しています。i < subjects.lengthの意味合いを理解できるかどうかが肝でしょうか。

 

4行目では、「0点から始まったトータルスコアに対して、繰り返すたびに教科ごとのスコアを和算しろ」という意味になります。

subjects[ i ].scoreの意味を理解することが大事です。

繰り返すたびにiは0から1ずつ増えていくので、最終的に英数国理社全てを読み出すことになります。.scoreにより、その時の配列からscoreキーに入っている値を読み出して、total_scoreに和算することになりますね。

 

 

if (total_score === 500){
console.log("ランクはAです。")
}else if(total_score >= 300 && total_score < 500 ){
console.log("ランクはBです。")
}else{
console.log("ランクはCです。")
}

よくあるif文ですね。

ほとんどrubyとおなじ。

elsif ではなくてelse ifと記述するところ、条件を都度( )で覆うところ、

実行処理を{ }で覆うところが違うところでしょうか。

ほとんど同じ感覚で使ってます。

 

最後の文

rank_judge()

一番最初にfunctionによるメソッドとして定義したものは、

実行するためにこうやって記載して呼び出してあげないと動いてくれません。

これもrubyと一緒ですね。

今回は引数を用いないので、( )の中は何もなしです。

 

こんなところかぁ・・・。

JavaScriptに関しては、概ねrubyの基礎があれば同じ感覚で使えるんじゃないかなと思います。

 

 

うおおおおおおお眠い、眠いぞ。

明日は正規表現を書きたい・・・。

意味わからんのじゃ・・・正規表現は・・・。苦手なんじゃ・・。

ruby/クラスとインスタンス

最近勉強ばっかりで書いてなかったので書こうかなと。

今日はrubyのクラスとインスタンスについて書こうかなと思うよ。

 

クラスとは

とある種類のオブジェクトにおける、共通の属性とメソッドをまとめて定義しておく型のようなもの。

ある属性のクラスからインスタンスを作成し、そのインスタンスで処理を行います。

これによってインスタンスの一貫性が生まれます。

 

さて全くわからん!なんのことだよって感じなんで、次はインスタンスについて調べます。

 

インスタンスとは

クラスに基づいて生まれたオブジェクトのことインスタンスと言います。

とにかくクラスが先。クラスからインスタンスが生まれます。

 

なんて言われてもやっぱりわからんよね・・。

ということでいつも通りとりあえず書いてみます。

 

 

class Fruits
@@fruits = "果物"#クラス変数

#Fruitsクラスのインスタンスメソッドsay_fruits
def say_fruits
puts "私は#{@@fruits}です。" #クラス変数はクラス内部であればどこでも呼び出すことができる。
end

#Fruitsクラスのインスタンスメソッドinitialize。initializeはクラスが生み出された時に同時に実行される。
def initialize
@name = "メロン"
@color = "緑"
end

#インスタンスメソッドに対してクラスメソッドと呼ばれる。
def self.taste
puts "甘いよ"
end

#クラス内部でのインスタンスメソッドでは、インスタンス変数を用いることができる。
def self_introduction
puts "私は#{@name}で色は#{@color}です。"
end

end

fruits = Fruits.new
fruits.say_fruits
Fruits.taste
fruits.self_introduction

こんな感じ。

ちなみに実行結果ははこんな感じ。

私は果物です。
甘いよ
私はメロンで色は緑です。

 

一個一個何してるかというと、まずこれ

class Fruits
@@fruits = "果物"#クラス変数
end

Fruitsというクラスを定義しています。

同時にこのクラス内でクラス変数@@fruitsを使うと、"果物"がかえってくるようにしてます。クラス変数は、fruitsクラスの内部であればどこでも使用可能です。

ちなみにクラスには継承というものもあるので、継承先でもクラス変数が使えます。

 

 

#Fruitsクラスのインスタンスメソッドsay_fruits
def say_fruits
puts "私は#{@@fruits}です。" #クラス変数はクラス内部であればどこでも呼び出すことができる。
end
frtuisクラス内部でインスタンスメソッドsay_fruitsを定義しています。
出力としては、クラス変数@@fruitsを出力することになります。
普段何気なく使ってるdefによるメソッド定義ですが、必ずクラスの内部に作成されている・・らしい。そんな実感ないけど・・・
 
 
#Fruitsクラスのインスタンスメソッドinitialize。initializeはクラスが生み出された時に同時に実行される。
def initialize
@name = "メロン"
@color = "緑"
end
initializeメソッドを定義。
インスタンスを作成するために、Fruits.new(後述)をするんだけど、その時に同時にinitializeメソッド内の処理をしてくれるっていう省エネ感が特徴。
ここではインスタンス変数@nameと@colorに"メロン"と"緑"を代入します。

ちなみにインスタンス変数はクラスのインスタンスにおいて使用することができます。

 

#インスタンスメソッドに対してクラスメソッドと呼ばれる。
def self.taste
puts "甘いよ"
end

self.(メソッド名)でクラスメソッドを定義できます。

クラスメソッドはクラス変数にアクセスするときに用いることが多いそうです。

例えば、外部ファイルからデータを取得してインスタンスを作成するときとか・・・らしい。

ここでは実行することで"甘いよ"って言います。果物のくせに生意気ですね。

 

#クラス内部でのインスタンスメソッドでは、インスタンス変数を用いることができる。
def self_introduction
puts "私は#{@name}で色は#{@color}です。"
end

最後にまたインスタンスメソッド。インスタンスメソッドでは、別のインスタンスメソッドで使われたインスンタンス変数を使うことができます。

ここでは"私はメロンで色は緑です。"と自己紹介します。果物のくせに生意気ですね。

 

これらは書いただけでは実行されず、下記の通りに記載することで初めて実行されます。

fruits = Fruits.new
fruits.say_fruits
Fruits.taste
fruits.self_introduction

furits = Fruits.newにより、fruitsというインスタンスが作成されます。

まずこのコマンドによってインスタンスを作成する必要があります。

その後は、

インスタンスメソッドであれば(インスタンス名).(インスタンスメソッド名)と書くことによって

クラスメソッドであれば(クラス名).(クラスメソッド名)と書くことで実行されます。

途中で出てきたinitializeメソッドは、Fruits.newと同時に実行されるので省略されます。

 

こんな感じか。

噛み砕くためにクラスという単語とインスタンスという単語を努めて使わないという初心は開始数行で消えました。

 

bundle installエラーの原因 gemファイルの保存場所がおかしい?

掲題について、前回も少しまとめたんだけど

原因これかも?というのがあったので自分のために走り書きを。

 

ターミナルにて、

bundle show rails

を実行すると

/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rails-4.2.6

というのが帰ってくるらしい。

どうやらこれがbundlerによって保存されるgemの場所とのこと、

 

自分のPCでこの指示を出すと、返ってくるのが、

/Users/ユーザー名/projects/rensyuutyuu/vendor/bundle/gems/rails-5.2.2.1

ここにgemが保存されてることになってる。

おそらく前者が標準gemファイル保存場所というやつで、後者がprojectごとにgemをインストールしてしまっている状態だと思う。

 

以前記事にまとめたbundle installの話は、標準ファイルに保存すればいいものをなんでお前はプロジェクトに保存しようとしてるんだ?っていうエラーだったのではないかという仮説。

 

ちなみに、現状のgemファイル保存状況では、まったく階層違いのrbファイルでrequireを使ってもクラスを読み取ることができなかった。

 

応急処置として、rbenv exec gem install gemファイル名

というコードで、rbenv exec gem listのlocal gemに入れることでひとまず対処できた。

 

 

忘れないうちに走り書きしておいた。

自分用メモと言えどひどい文章だ。わかりにくい。わかってないけど。