20200515のJavaScriptに関する記事は30件です。

【JavaScript】関数、無名関数、即時関数の違い、書き方を覚えるために実施したこと

【JavaScript】【初学者向け学習法】関数、無名関数、即時関数の違い、書き方を覚えるために実施したこと

私と同じようにこれからJavaScriptを学習する/している方向けの記事です。
タイトルの通り、関数、無名関数、即時関数を理解するために実施した学習方法です。
もし同じレベル感でお悩みの方の参考になれば幸いです。

背景:
プログラミング学習には、手を動かすのが大切と言われます。
動画を見ながらエディタで手を動かすのですが、動画の速度に追いつかず、タイピングもどきで終わることがありました。タイピングに終始していると理解が追いつきません。
そんな自分の学習効率を改善するために考えた方法です。

やったこと:
事前準備:
・事前に解説動画を見る
・一緒に画面の処理をタイピングするが、追いつかない場合は解説の理解に集中する
・正解を複写し、コメントアウトで控えておく

①模写した正解を見ながらエディタに式を書く
②Google Chromeのデベロッパーツールで表示させる
③なぜ表示されたのかを自分に説明する。できないうちは①②の複写を繰り返す
④①の内容を変えて書き、以降繰り返し

①は手を動かすことで記憶する
③は自分への説明で記憶を定着させる
④は変化を与えることで学習効果を高める
ということをしています。

具体例です。

    function hello(name){
        var Hello = "hello " + name;
        return Hello;
    }

    var result = hello("Tom");
    console.log(result);

ここで①②を実行し、結果を確かめたうえで、③で自分に説明します。
模写で精いっぱいだと説明できないので①②を繰り返します。

模写を見ないでできるようになると、処理内容を考える余裕が生まれます。
そうすると③の自分への説明に進めます。
この場合だと3つに分けて説明しました。

・hello関数は関数内でローカル変数Hello変数の処理内容を作り、returnで内容を出力する
・result変数はhello関数から処理のバトンを受けとった
・console.logでresult変数を表示した

※声に出した場合は、音声情報として記憶の定着効果が高まります
次に④である変化として無名関数で処理してみました。

    var hello = function(name){
        var Hello = "hello " + name;
        return Hello;
    }

    var result = hello("rachel cook");
    console.log(result);

①②で模写ができるようになっているので、改変する余裕があります。
こちらも自分に説明できたら、次は変化の幅を変えて、別の処理内容を書きます。
文字列の表示から四則演算に変えました。

    function add(a,b){
        var Add = a + b;
        return Add;
    }
    var result = add(1,2);
    console.log(result);

次の変化は引数の数を増やした無名関数Ver.
ローカル変数名に計算を意味するcalcutlationと命名し、英語学習も兼ねました。

    var add = function(x,y,z){
        var calculation = x + y + z;
        return calculation;
    }
    var result = add(10,11,12);
    console.log(result);

次は関数と即時関数の変化を書きました。

    function hello(){
        console.log("hello");
    };
    hello();
    (function hello(){
        console.log("hello there.");
    })();

ここに至るまでに関数の形を手が覚えているので、()で括る箇所も理解して使うことができました。

そして学習フローをこのように記事にすることで、自分の理解度を確認。
関数以外の学習にも応用できるようにと考えた次第です。
こうした方が効率が良い、という知恵がありましたら、コメントをお寄せ頂けますと幸いです。

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

VSCodeでReact-JSXを保存するとき勝手に改行されちゃう問題

問題

VSCodeで下記のようなDOMを書いて

ReactDOM.render(
  <React.StrictMode >
   <App />
  </React.StrictMode>,
  document.getElementById('root')
);

保存(⌘+S)しようとすると、勝手に改行されてしまう問題。
もちろんエラーになります。

ReactDOM.render(
  <
React.StrictMode >
  <
  App / >
  <
  /React.StrictMode>,
  document.getElementById('root')
);

解決策

VSCodeの設定を変更します。
Settings.jsonファイルを開き、下記を追加します。

    "files.associations": {
        "*.js": "javascriptreact"
    }

以上です。

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

Vuetfiy v-dialogのモーダル化が機能しない

Vuetify v-dialogのモーダル化が機能しない

新規で作成もしくはソースコードの内容を大体把握してある個人の方・一人でプロジェクトを回している方には参考にならないかと思います。

Vuetifyのv-dialogにはオプションで色々指定できる
モーダル化させるためにpersistentを指定することでダイアログ外を押しても閉じないようにできる

それが機能しなかったので備忘録的にまとめる

Vuetify
https://vuetifyjs.com/ja/components/dialogs/

結論

outsideというオプションが悪さしていました。
このオプションはダイアログ外を押した時に発火するイベントです。
そいつが諸悪の権化なので削除するなりよしなに修正することで回避しましょう。

こんなことで時間取られるの辛い…辛い…

ちなみに調べるとVuetify2.1以下だとバグで機能しないらしいですが、orverlayと合わせるとバグるみたいなので多分今回の場合は関係ないです。
https://github.com/vuetifyjs/vuetify/issues/8697

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

Google maps APIでドラゴンボールの場所を検索してみよう!

はじめに

  • ピンを快適にする方法
  • Geocodingとは
  • 実装

ピンを快適にする方法

前回はピンを快適にする方法を記事で紹介しました!前回からの続きとなりますので是非参考にしてください!!
https://qiita.com/Ryunosuke-watanabe/items/958ed12471effc8fd778

Goecodingとは

ジオコーディングとは住所や地名から緯度経度の情報を出すことです!
この機能を使えば、具体的な緯度経度がわからなくても、場所の名前だけでピンを刺したり中心を設定することができます。

実装

JavaScript,HTML,css全てでで実装します!まずGeocodingをするにはAPIでGeocodingを追加する必要があります。
プラットフォームの方で簡単に追加できるので是非やってみてください。

まずhtmlとcssで検索窓を追加します。

maps.html
<html>
<head>
    <meta charset="UTF-8">
    <title>map</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div id="floating-panel">
        <input id="address" type="textbox" value="東京駅">
        <input id="submit" type="button" value="Geocode">
    </div>
    <div id="map"></div>
    <script src="js/map.js"></script>
    <script src="data/place.json"></script>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBZUNbx_85VM6LurRVIHZcxl-7Vg3O2C9g&callback=initMap" async defer></scriptsrc>></script>
</body>
</html>
style.css
body {
    height: 100%;
    width: 100%;
}

#map {
    height: 100%;
    width: 100%;
}

.mapsballoon {
    width: 150px;
    height: auto;
}

#floating-panel {
    display: inline-block;
    background-color: #fff;
    padding: 5px;
    border: 1px solid #999;
    text-align: center;
    font-family: 'Roboto','sans-serif';
    line-height: 30px;
    padding-left: 10px;
}

これで検索窓が追加されました!!
image.png

次はJavaScriptで機能を実装していきます!

map.js
var map;
var marker = [];
var infowindow = [];
var Center = {lat: 24.4064, lng: 124.1754};
var place_data = [
    {
        "loc": "バンナ公園",
        "lat": 24.375031,
        "lng": 124.160795,
        "balloon": "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"
    },
      {
        "loc": "石垣島鍾乳洞",
        "lat": 24.361743,
        "lng": 124.154466,
        "balloon": "test"
    },
      {
        "loc": "石垣やいま村",
        "lat": 24.40489,
        "lng": 124.144636,
        "balloon": "test"
    }
]

function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
        center: Center,
        zoom: 11.5
    });
    markerset()
};

function markerset(){
    for(var i=0;i<place_data.length;i++) {
        marker[i] = new google.maps.Marker({
            position: {lat: place_data[i].lat, lng: place_data[i].lng},
            map: map,
            title: place_data[i].loc
        });

        infowindow[i] = new google.maps.InfoWindow({
            content: '<div class="mapsballoon"><h2>' + place_data[i].loc + '</h2>' + place_data[i].balloon + '</div>'
        });
        var geocoder = new google.maps.Geocoder();
        markerEvents(i)
        document.getElementById('submit').addEventListener('click', function() {
            geocodeAddress(geocoder, map);
        });
    }
}

var opened = place_data.length + 1;
function markerEvents(i) {
    marker[i].addListener('click', function() {
        if (opened != place_data.length + 1) {
            infowindow[opened].close(map, marker[opened]);
        }
        infowindow[i].open(map, marker[i]);
        opened = i;
    });
}


function geocodeAddress(geocoder, resultsMap) {
    var address = document.getElementById('address').value;
    geocoder.geocode({'address': address}, function(results, status) {
      if (status === 'OK') {
        resultsMap.setCenter(results[0].geometry.location);
        var marker = new google.maps.Marker({
          map: resultsMap,
          position: results[0].geometry.location
        });
      } else {
        alert('Geocode was not successful for the following reason: ' + status);
      }
    });
}

今回はgeocodeAddressというファンクションを追加しました!
うまくhtmlのIDと紐付けて検索できるようにしています!!

image.png

ちゃんと東京駅に飛ぶことができました!
いろいろな場所に飛ぶことができるので是非遊んでみてください!!

最後に

いかがだったでしょうか?今回は前回の予告通りgeocodingの機能を実装してみました。いろいろな実装に使えそうな機能ですので是非参考にしてください。
次回はピンにラベルをつけていきたいと思います!
参考文献
https://developers.google.com/maps/documentation/javascript/examples/geocoding-simple

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

JavaScriptだけでtodoアプリを作る(JS基礎復習)

家にいることが多くなったこのご時世。
比較的自由な時間が増えたので、全然触っていなかったJavaScriptを思い出すため、基礎の復習に最適だと聞くTodoアプリを作ってみました。

todoアプリに実装する機能

todoアプリに実装する機能は、以下の通りです。

  • テキストボックスに入力した文字を取得する
  • addボタンを押した時
    • テキストボックスに入力した内容がToDoリストに表示される
    • 一項目を削除するボタン、全ての項目を削除するボタンを表示する
  • ToDoリストに追加される項目の隣に✅(完了ボタン)を表示
  • ✅(完了ボタン)を押した時
    • Doneリストにその項目を表示する
    • 項目の隣に削除ボタン、✅を押した日時を表示する

こちらが完成図になります。
スクリーンショット 2020-05-14 12.03.07.png
こんな条件で、todoアプリを作ってみます。

Step1.まずは見た目から

まずはHTMLとCSSで、見た目の部分だけ作ります。
HTMLは、

todoappforjs

<body>
    <header>
        <h1>ToDoアプリ</h1>
    </header>
    <div class="addli">
        <input id="add-area" type="text" placeholder="To Do">
        <span class="add-btn">add</span>
    </div>
    <div id="todo-app">
        <ul class="lists" id="todo">
            <p>ToDo</p>
        </ul>
        <ul class="lists" id="done">
            <p>Done</p>
        </ul>
    </div>
</body>

以上がHTMLです。CSSは割愛します。

スクリーンショット 2020-05-06 19.00.01.png
見た目はこんな感じ。

Step2.textboxに入力した内容がToDoに表示されるまで

ここからJavaScriptを記述します。

//class 'add-btn' を取得
var e = document.getElementsByClassName('add-btn');

e[0].addEventListener('click', function() {
    var listText = document.getElementById('add-area');
    var text = document.createTextNode('listText.value');
    var li = document.createElement('li');
    var list = document.getElementById('todo');
  //作成したliタグにtextboxに入力した値textを追加
    li.appendChild(text);
    list.appendChild(li);
});

addボタンをクリックした時に、inputタグに入力されている値のvalueを取得し、新たにliタグをToDoリスト内に作り、そこに先ほどのinputタグに入力した値を追加するという内容です。

step3.削除ボタン、完了ボタンの実装まで

続いて、作成したliタグに削除ボタン、完了ボタンを追加します。ちなみに、ゴミ箱のアイコンとチェックマークは、Font Awesomeを使っています。同様にJavaScriptから、

e[0].addEventListener('click', function(){
var li = document.createElement('li');
//削除ボタン
var trash = document.createElement('span');
trash.classList.add('trash');
trash.innerHTML = '削除 <i class="fas fa-trash-alt"></i>';
li.appendChild(trash);

//完了ボタン
var check = document.createElement('span');
check.classList.add('check');
check.innerHTML = '完了 <i class="far fa-check-square"></i>';
li.appendChild(check);
var list = document.getElementById('todo');
list.appendChild(li)

}) 

ここまでのJavaScriptはaddボタンを押した時のイベントを記述しているので、中身を一つの関数にまとめます。

function addElement() {
    var text = document.createTextNode('listText.value');
    var li = document.createElement('li');
    var list = document.getElementById('todo');
  //作成したliタグにtextboxに入力した値textを追加
    li.appendChild(text);
    list.appendChild(li);
 //削除ボタン
    var trash = document.createElement('span');
    trash.classList.add('trash');
    trash.innerHTML = '削除 <i class="fas fa-trash-alt"></i>';
    li.appendChild(trash);

 //完了ボタン
    var check = document.createElement('span');
    check.classList.add('check');
    check.innerHTML = '完了 <i class="far fa-check-square"></i>';
    li.appendChild(check);

};

削除ボタン、完了ボタンにそれぞれクラスを割り当てたのは、判別化を図るためです。
innerHTMLプロパティはそのままHTMLを書くことができるので便利ですね。

step4.要素を削除する

続いて、削除ボタンを押した時に要素を削除するイベントを記述します。押した要素を判別するのにfor文を使います。

function trashBox() {
   var trash = document.getElementsByClassName('trash');
   for(var i = 0; i < trash.length; i++) {
       trash[i].addEventListener('click', function() {
              this.parentNode.remove();
       });
   }
}

削除ボタンを押すことで、その押したリストのみが削除される、といった内容の関数です。

step5.Doneリストに要素を移動する

続いて、完了ボタンを押した時、Doneリスト内に移動する、というイベントを記述します。
こちらも、削除ボタンと同様にfor文でまわします。

function checkBox() {
    var check = document.getElementsByClassName('check');
    var done = document.getElementById('done');
    for(var i = 0; i < check.length; i++) {
        check[i].addEventListener('click', function() {
            done.appendChild(this.parentNode);
        });
    }
}

JSのコードまとめ

最後に、まとめとしてこれまでのコードの記述の一覧を載せます。

var e = document.getElementsByClassName('add-btn');

e[0].addEventListener('click', function() {
    addElement();
    trashBox();
    checkBox();
});

function addElement() {
    var listText = document.getElementById('add-area');
    var text = document.createTextNode(listText.value);
    var li = document.createElement('li');
    li.appendChild(text);


    // li要素にボタンを追加
    var trash = document.createElement('span');
    trash.classList.add('trashPosition');
    trash.classList.add('trash');
    trash.innerHTML = '削除 <i class="fas fa-trash-alt"></i>'
    li.appendChild(trash);

    var check = document.createElement('span');
    check.classList.add('checkPosition');
    check.classList.add('check');
    check.innerHTML = '完了 <i class="far fa-check-square"></i>'
    li.appendChild(check);


    var lists = document.getElementById('todo')
    lists.appendChild(li);
};

// todoリスト内の削除ボタン
function trashBox() {
    var trash = document.getElementsByClassName('trash');
    // クリックしたliタグの配列数を取得
    for (var i = 0; i < trash.length; i++) {
        trash[i].addEventListener('click', function() {
            // thisはtrash[i]にあたる
            var li = this.parentNode;
            li.remove();
            // console.log(i);
        });
    };
};

function checkBox() {
    var check = document.getElementsByClassName('check');
    var done = document.getElementById('done');
    for (var i = 0; i < check.length; i++) {
        check[i].addEventListener('click', function() {
            var li = this.parentNode;
            done.appendChild(li);
            this.remove();
        });
    };
};

終わりに

以上がJavaScriptのみで作るTodoアプリでした。基礎的な部分のみを理解して作ったものなので、どこか間違ってる箇所や、より効率的にかけるところがあれば、ご指摘いただけるとありがたいです。

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

JavaScriptでディープコピーしたい時

はじめに

モーダルを使うときなど、オブジェクトのディープコピーを作りたくなることがたまにあり、
調べても自分が期待する結果になるような方法がなかったので、備忘録的にまとめておこうと思います。
間違いやもっと良い方法などありましたら、コメントいただけると嬉しいです!

実現したいこと

下記のようにArrayの中に複数オブジェクトがあるデータを良い感じにコピーしたい。。

[{'key1': 'value1', 'key2':'value2'},{'key1': 'value3', 'key2': undefined}]

なぜディープコピーする必要があるか

上記をコピーしてコピー先のオブジェクトを変更してみると・・
追記(2020/5/16)
@jay-es さんからコメントをいただき、シャローコピーするように修正しました!
コメントありがとうございます!

let lists = [{'key1': 'value1', 'key2':'value2'},{'key1': 'value3', 'key2': undefined}]
let copyLists = [...lists]
copyLists[0].key1 = "hoge"
console.log('lists')
console.log(lists)

console.log('copyLists')
console.log(copyLists)

実行結果

lists
0: {key1: "hoge", key2: "value2"}
1: {key1: "value3", key2: undefined}

copyLists
0: {key1: "hoge", key2: "value2"}
1: {key1: "value3", key2: undefined}

このようにコピー先のオブジェクトを変更したところ、コピー元のオブジェクトまで更新されてしまいました。
上記のようなコピーの仕方の場合、listsとcopyListsがメモリ上の同じデータを参照してしまっているためです。
(細かいところは自分も理解し切れていないのですが、上記の場合はコピーして別のオブジェクトを作ったというより、
同じオブジェクトに対して別名を付けているようなもの?と理解しています)
一方ディープコピーの場合は、オブジェクトとメモリ上のデータの両方をコピーするため、独立したオブジェクトを作ることができます。

ディープコピーを試してみた

方法1

ネットで検索するとよく出てくるのが、JSON.parse(JSON.stringify())を使用したやり方。
まずはこのやり方を試してみました。

let lists = [{'key1': 'value1', 'key2':'value2'},{'key1': 'value3', 'key2': undefined}]
let copyLists = JSON.parse(JSON.stringify(lists))

copyLists[0].key1 = "hoge"
console.log('lists')
console.log(lists)

console.log('copyLists')
console.log(copyLists)

実行結果

lists
0: {key1: "value1", key2: "value2"}
1: {key1: "value3", key2: undefined}

copyLists
0: {key1: "hoge", key2: "value2"}
1: {key1: "value3"}

確かにコピー先のオブジェクトを変更しても、コピー元のオブジェクトは更新されなくなったが、
key2がなくなっていることから、値がundefinedの場合はキーごと消えてしまうよう。。
調べてみたところ、こちらの記事で解説があり、
どうやら特定のオブジェクトのプロパティだった場合、消されてしまうようです。

方法2

Array.prototype.map()で新しいオブジェクトを作るパターン

let lists = [{'key1': 'value1', 'key2':'value2'},{'key1': 'value3', 'key2': undefined}]
let copyLists = lists.map( list => ({'key1': list.key1, 'key2': list.key2}))
copyLists[0].key1 = "hoge"
console.log('lists')
console.log(lists)

console.log('copyLists')
console.log(copyLists)

実行結果

lists
0: {key1: "value1", key2: "value2"}
1: {key1: "value3", key2: undefined}

copyLists
0: {key1: "hoge", key2: "value2"}
1: {key1: "value3", key2: undefined}

コピー先のオブジェクトを変更しても、コピー元のオブジェクトは更新されず、
値がundefinedになっているオブジェクトもディープコピーできました!

追記(2020/5/16)
Array.prototype.map()とスプレット構文を使う方法
@pochopocho13 さんからコメントで教えていただきました!ありがとうございます!)

let lists = [{'key1': 'value1', 'key2':'value2'},{'key1': 'value3', 'key2': undefined}]
let copyLists = lists.map( list => ({...list}))
copyLists[0].key1 = "hoge"
console.log('lists')
console.log(lists)

console.log('copyLists')
console.log(copyLists)

実行結果

lists
0: {key1: "value1", key2: "value2"}
1: {key1: "value3", key2: undefined}

copyLists
0: {key1: "hoge", key2: "value2"}
1: {key1: "value3", key2: undefined}

スプレット構文を使った方法であれば、仮にkeyが変わった場合でも、
修正が不要になるのでこちらの方が良さそうです!

まとめ

この辺は地味にハマるところなので、徐々に理解を深めていきたいと思いました。。

参考にさせていただいた記事

JavaScriptのDeepCopyでJSON.parse/stringifyを使ってはいけない
[JavaScript]色々なディープコピー

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

constで宣言した配列の値は変えられるのか(JavaScript)

結論

タイトルの答えは「YES。変えられる。」

理由

JavaScriptでconstの配列の値が変わる理由」という記事を読んだところ、

constの値は不変ということではなく、変数識別子が再代入できないというだけだからだ。

というのが結論だったが、実際の挙動も確かめたかったので手を動かしてみた。

実行できるパターン

1.インデックスを指定した値の代入

const arr1 = [];
arr1[0] = 'a';
console.log(arr1);

// 実行結果
// [ 'a' ]

2. 要素の追加

const arr2 = [];
arr2.push('b');
console.log(arr2);

// 実行結果
// ['b']

3.要素の削除

const arr3 = [1, 2, 3];
arr3.splice(1, 1);
console.log(arr3);

// 実行結果
// [ 1, 3 ]

4. 参照している配列への要素の追加

const arr4 = [1, 2, 3];
const arr5 = arr4;
arr5[0] = 'c';
console.log(arr4);
console.log(arr5);

// 実行結果
// ['c', 2, 3]
// ['c', 2, 3]

実行できないパターン

配列自体への再代入

const arr6 = [1, 2, 3];
arr6 = [4, 5, 6];

// 実行結果
// arr6 = [4, 5, 6];
//      ^
//   TypeError: Assignment to constant variable.

まとめ

配列の中身の操作や再代入に関しては、要素を追加しようが消そうが怒られることなく実行できた。
ただし「配列それ自体への再代入」はできなかった。

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

Vueのカスタムコンポーネントで双方向データバインディングを入れてみた

Vueのカスタムコンポーネントはすごく便利ですね。
HTMLのテンプレートとして使えて、しかも使う側がさらにHTMLを差し込むことができるのは重宝しています。

今回は、まずはカスタムコンポーネントを単純なテンプレートとして使う例を示した後、さらに汎用的にするために、カスタムコンポーネントを双方向データバインディングに対応させます。

ちなみに、ここらへんVueの方々が頑張っていただいているようで、仕様が変わる(使いやすくなる)ことがありますので、その際にはまた追従したいと思います。

最後に、自作のswagger定義ファイルエディタを紹介しています。

単純なコンポーネントの例:HTMLテンプレートとして使う

HTMLのテンプレートとして使い、使う側がさらにHTMLを差し込む例です。
期待はこんな感じです。

使う側はこんな風に書きます。

<custom-template>
    使う側が差し込みたいHTML
</custom-template>

そして、テンプレート側ではこんな感じで記載しておきます。

<div class="panel panel-default">
    <slot></slot>
</div>

最終的に、こんな感じに合成したいです。

<div class="panel panel-default">
    使う側が差し込みたいHTML
</div>

実際のテンプレート側は、以下のようなコードとなります。

Vue.component('custom-template-01', {
    template: `
        <div class="panel panel-default">
            <slot></slot>
        </div>`,
});

そうすると、使う側には以下のように合成されて見えるようになります。<slot></slot>が挿し代わっています。

    <custom-template-01>
      <div class="panel-body">
        Hello World
      </div>
    </custom-template-01>

ちなみに、使う側が差し込んだHTMLは使う側の制御範囲なので、v-modelなどそのまま使えます。
例えばこんな感じ。(使う側です)

    <label>data</label> {{data}}
    <br>
    <label>custom_template_01_a</label>
    <custom-template-01>
      <div class="panel-body">
        <input type="text" v-model="data" class="form-control">
      </div>
    </custom-template-01>

以降では、使う側を親、使われる側(HTMLテンプレート側)を子と呼ぶようにします。

HTMLテンプレート例でのデータバインディング

さきほどの、HTMLテンプレート例では、子の<slot></slot>の内容をすべて親に任せています。一方で、親からのパラメータを使って子が内容を作成したいことが多々あります。

そこで、もう一つ例を挙げます。まず子の方です。

Vue.component('custom-template-02', {
    props: ['header'],
    template: `
        <div class="panel">
            <div class="panel-heading">
                <h4 class="panel-title">{{header}}</h4>
            </div>
            <div class="panel-body">
                <slot></slot>
            </div>
            <div class="panel-footer">
                <slot name="footer"></slot>
            </div>
        </div>`,
});

親の方はこちらです。

    <custom-template-02 class="panel-default" header="This is Header">
      <template>
        Hello World
      </template>
      <template v-slot:footer>
        since 2020
      </template>
    </custom-template-02>

この例では、最初の例に比較して、追加の仕組みを3つ使っています。

・1つめ:差し込み先のslotに名前を付けました。

テンプレート内のname=footerの属性 が付いたslotエレメントが、v-slot:footer の属性を付けたtemplateエレメントの中身に置き換わります。footerという名前は自由に決められます。名前を付けたことで、差し込む場所を複数作ることができるようになります。

・2つめ:親から子にパラメータを渡しています。

子に、props: [ ‘header’] というプロパティが増えています。
これは、子側は親側からheaderというプロパティを受け取ることを宣言しています。

親は、header="This is Header" という感じで、カスタムコンポーネントの要素にheaderを追加していますので、それを子が受け取ることができています。
受け取った値は、

            <div class="modal-header">
                <h4 class="modal-title">{{header}}</h4>
            </div>

のような感じで、HTMLの中に含めているのがわかります。
これにより、すべてを親側に任せるだけでなく、親からパラメータを取得して子側でHTMLに反映することができます。

・3つ目:親で指定した属性が子のHTMLに渡されます。

class="panel-default" の部分ですが、headerと異なり、子側で受け取る準備(props指定)をしていません。
その場合、HTMLのテンプレートのルートのエレメントの属性として追加されます。

テンプレート上は、

        <div class="panel">

となっていますが、実際のHTMLに描画されたときには、

        <div class="panel panel-default">

となります。子側にclassがすでに指定済みですので、子側のclassに親からのclassの属性値が追加された形になっています。当然ながら、propsに指定済みの属性は除きます。

親と子の双方向データバインディング

propsを使って親のデータを子に渡しました。
また、slotを使って子がHTMLを生成する一部を親に任せることができました。
しかしながら、今まで説明してきた方法では子で処理した結果を親が受け取ることはできません。

何をいっているわからないかもしれませんが、Vueで必ず使う双方向データバインディングであるv-modelが使えていないということです。

inout_textのエディットボックスで入力した値を初期値とし、カスタムコンポーネント側でダイアログを表示して初期値を表示し、別の値を入力してもらって、その値をinout_textのエディットボックスに表示したい例です。
以下が期待する親のHTMLです。

    <input type="text" v-model="inout_text" class="form-control">

    <custom-template-03 class="panel-default" header="This is Header" v-model="inout_text">
      <template>
        Input Dialog Test
      </template>
    </custom-template-03>

少し分解すると、Vueとしてv-model=”inout_text”は以下に置き換えられます。

  v-bind:value=”input_text” v-on:input=”inout_text”

valueという名前で子に渡し、inputというイベントで親に返してもらえればよいわけです。

まず、valueの子への渡し方はすでに説明しました。
propsにvalueを追加すればよいだけです。
残るは、inputイベントです。

それには、子で以下を呼べばよいのです。

this.$emit(input, 返したい値)

それを反映したのがこちら。

Vue.component('custom-template-03', {
    props: ['value'],
    template: `
        <div class="panel">
            <div class="panel-body">
                <slot></slot>
            </div>
            <div class="panel-footer">
                <button class="btn btn-default" v-on:click="do_input">do_input</button>
            </div>
        </div>`,
    methods:{
        do_input: function(){
            var ret = window.prompt('入力してください。', this.value);
            if( ret )
                this.$emit('input', ret);
        }
    }
});

親から受け取ったthis.valueを子で書き換えたら、そのまま親にイベントが伝わってほしいかもしれません。ですが、this.valueやemitで、親からの子のデータバインディングと子から親へのイベントを組み合わせて実現しているだけで、それぞれの要素は、片方向でしかないのです。

子と孫の双方向データバインディング

HTMLテンプレート例では、親が子のカスタムコンポーネントを使っていました。
実際には、親が子のカスタムコンポーネントを使って、子がさらに孫のカスタムコンポーネントを使って、というように、階層的につながっていく場合が多々あります。
そうすると、子の中でもv-modelを使いたくなります。

まず、孫から。

Vue.component('custom-template-04-a', {
    props: ['value'],
    template: `
        <div>
            <button class="btn btn-default" v-on:click="do_input">do_input</button>
        </div>
    `,
    methods:{
        do_input: function(){
            var ret = window.prompt('入力してください。', this.value);
            if( ret )
                this.$emit('input', ret);
        }
    }
});

さきほど子側で、入力ダイアログを出していた部分を抜き出したものです。
次が子側です。孫に対してv-modelを使っています。

Vue.component('custom-template-04', {
    props: ['value'],
    template: `
        <div class="panel">
            <div class="panel-body">
                <slot></slot>
            </div>
            <div class="panel-footer">
                <custom-template-04-a v-model="value_"></custom-template-04-a>
            </div>
        </div>`,
    data: function(){
      return {
          value_: this.value,
      }
    },
    watch: {
        value: function(newValue){
            this.value_ = newValue;
        },
        value_: function(newValue){
            this.$emit('input', newValue);
        }
    }
});

親から受け取ったthis.valueは、親から子への片方向専用であり、子の中での孫との双方向データバインディングには使うことができません。
そこで、親から受け取ったthis.valueをthis.value_にコピーして、それを孫との双方向データバインディングに使っています。

    data: function(){
      return {
          value_: this.value,
      }
    },

の部分で、孫とのv-modelに使う変数を宣言し、(コンポーネントの場合、dataは関数で返さないといけないです)

    watch: {
        value: function(newValue){
            this.value_ = newValue;
        },

によって、親からvalueの変更通知を受け取ったら、this.value_に再コピーしています。
一方で、以下の孫のカスタムコンポーネント側でthis.value_値の更新通知が来ます。

    <custom-template-04-a v-model="value_"></custom-template-04-a>

そのイベントも、watchでthis.value_を監視することでフックしています。
トリガーされると、親に変更された新しい値を伝えています。

    watch: {
・・・
        value_: function(newValue){
            this.$emit('input', newValue);
        }
    }

まとめると、以下の形を覚えておけば、親-子-孫の間の双方向データバインディングを実現できそうです。

Vue.component('カスタムコンポーネント名', {
    props: ['value'],
    template: `
   表示したいHTML
    `,
    data: function(){
      return {
          value_: this.value,
      }
    },
    watch: {
        value: function(newValue){
            this.value_ = newValue;
        },
        value_: function(newValue){
            this.$emit('input', newValue);
        }
    }
});

以上でVueコンポーネントの実験は終わりです。

以下に、上記を確認できるページを用意しました。

Vueコンポーネント実験室
 https://poruruba.github.io/vuecomp_laboratory/labo_01/

image.png

サンプルアプリ:Swagger定義ファイルエディタ

さきほど作った双方向データバインディングの定型を使って、Swagger定義ファイルのエディタを作ってみました。

Web上から、エントリポイントを作成したり、メソッドを追加して、複数のパラメータを作ったりしできるようにしました。
(かなり端折っていますし、バグもたくさんあるとは思いますが、自己満足です。。。)

画面はこんな感じです。
お決まりの、Petstoreをサンプルとして読み込むボタンを用意したので、なんとなくイメージはできるかもしれません。

image.png

image.png

poruruba/vuecomp_laboratory
 https://github.com/poruruba/vuecomp_laboratory/

以下からアクセスできます。
 https://poruruba.github.io/vuecomp_laboratory/swagger_editor/

以上

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

【Nuxt.js】Modal実践編:QueryでModalを管理する②

前置き

picture_pc_8be74d31bdb9a712682ef9abdc34e5a9.gif

前回の続きです?
https://note.com/aliz/n/n47b0d98be524

1つのcomponentsで
複数のModalを表示させましょう✨

Step5: 中身をcomponents分けする

Modalの外側と内側で
componentsを切り分けていきましょう。
queryによって切り替えているpタグを
organismsに移動させます??
propsを使わなくて済むので
本当にただ移動させるだけです、楽ちん♪

file
components/
--| organisms/
----| modals/
-----| ModalContainer.vue
--| templates/
----| modals/
-----| ModalRoute.vue

layouts/
--| default.vue
ModalContainer.vue
<template>
 <div class="modal-container">
   <!-- 切り替える中身 -->
   <p
     v-if="$route.query.modal === 'login'"
     class="text"
   >
     {{ $route.query.modal }}
   </p>
   <p
     v-if="$route.query.modal == 'register'"
     class="text"
   >
     {{ $route.query.modal }}
   </p>
 </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
 name: 'Modal',
})
</script>

<style lang="scss" scoped>
 .modal-container {
   .text {
     font-size: 36px;
   }
 }
</style>

Step6: 中身をFormにする

picture_pc_8be74d31bdb9a712682ef9abdc34e5a9.gif

ついでにformを作りましょう?
password入力のinputも作ると
inputをmoleculesで作る必要が出てくるので
一旦イメージだけできればOKです??

ModalContainer.vue
<template>
 <div class="modal-container">
   <!-- 切り替える中身 -->
   <form
     class="form"
     v-if="$route.query.modal === 'login'"
     @submit.prevent
   >
     <label class="label">
       <span class="label">
         {{ $route.query.modal }}
       </span>
       <input
         v-model="form.email"
         :type="type"
         placeholder="email"
       >
     </label>
     <button
       class="button"
       type="submit"
       @click="$emit('submit', form)"
     >
       {{ $route.query.modal }}
     </button>
   </form>
   <form
     class="form"
     v-if="$route.query.modal === 'register'"
     @submit.prevent
   >
     <label class="label">
       <span class="label">
         {{ $route.query.modal }}
       </span>
       <input
         v-model="form.email"
         :type="type"
         placeholder="email"
       >
     </label>
     <button
       class="button"
       type="submit"
       @click="$emit('submit', form)"
     >
       {{ $route.query.modal }}
     </button>
   </form>
 </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
 name: 'Modal',
 data () {
   return {
     form: {
       email: '',
     },
   }
 },
 props: {
   type: {
     type: String,
     default: 'text',
   },
 },
})
</script>

<style lang="scss" scoped>
 .modal-container {
   .form {
     .label {
       font-size: 24px;

       .label {
         display: block;
       }
     }

     .button {
       display: block;
     }
   }
 }
</style>

【解説】
@click="$emit('submit', form)"
 inputに入力する値formを$emitで渡します。

ModalRoute.vue
<template>
 <div
   v-if="$route.query.modal"
   class="modal-route"
 >
   <div
     class="bg"
     @click="$router.push('/')"
   />
   <div class="modal-wrap">
     <button
       class="button"
       @click="$router.push('/')"
     >
       <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z" /></svg>
     </button>
     <ModalContainer
       @submit="submit"
     />
   </div>
 </div>
</template>

<script>
import Vue from 'vue'

export default Vue.extend({
 name: 'Modal',
 components: {
   ModalContainer: () => import('@/components/organisms/modals/ModalContainer.vue'),
 },
 methods: {
   submit (form) {
     console.log(form) // eslint-disable-line
   },
 },
})
</script>

<style lang="scss" scoped>
 .modal-route {
   position: fixed;
   top: 0;
   width: 100%;
   height: 100%;

   .bg {
     width: 100%;
     height: 100%;
     background-color: rgba(0,0,0,0.5);
   }

   .modal-wrap {
     border-radius: 8px;
     background-color: #ffffff;
     width: 50%;
     height: 50%;
     position: absolute;
     top: 50%;
     left: 50%;
     transform: translate(-50%, -50%);
     padding: 30px;

     .button {
       border: none;
       position: absolute;
       top: 5%;
       right: 2%;
     }
   }
 }
</style>

【解説】
・@submit
 $emitでつけたイベント名
・// eslint-disable-line
 consoleなど特定の行に書かないと
 ESLintでエラーになります。
 https://qiita.com/nju33/items/2d0cfea4fffbfdbff87a

Step7: Formを分ける

今はformが2種類ですが
もし増えたら管理が面倒なので
分けてしまいましょう?

え、1つのコンポーネントを
queryで切り替えるのが
メリットなんじゃないの??
分けたら意味なくない??
と思ったそこのアナタ❗️

安心してください?
componentタグを使えば良いのです✨

コンポーネント の基本

【ディレクトリ 】
modalsというファイルを作り
ModalContainerのform2つを
それぞれに分けましょう。

?before?

file
components/
--| organisms/
----| modals/
-----| ModalContainer.vue
--| templates/
----| modals/
-----| ModalRoute.vue

layouts/
--| default.vue

?after?

file
components/
--| templates/
----| modals/
-----| ModalRoute.vue

modals/
--| login.vue
--| register.vue

layouts/
--| default.vue

【modals/login.vue】
・formのregister部分を除きましょう?
・$emitのイベント名を分かりやすく
 submitLoginに変更しましょう!

login.vue
<template>
 <div class="modal-container">
   <!-- 切り替える中身 -->
   <form
     v-if="$route.query.modal === 'login'"
     class="form"
     @submit.prevent="$emit('submitLogin', form)"
   >
     <label class="label">
       <span class="label">
         {{ $route.query.modal }}
       </span>
       <input
         v-model="form.email"
         :type="type"
         placeholder="email"
       >
     </label>
     <button
       class="button"
       type="submit"
     >
       {{ $route.query.modal }}
     </button>
   </form>
 </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
 props: {
   type: {
     type: String,
     default: 'text',
   },
 },
 data () {
   return {
     form: {
       email: '',
     },
   }
 },
})
</script>

<style lang="scss" scoped>
 .modal-container {
   .form {
     .label {
       font-size: 24px;

       .label {
         display: block;
       }
     }

     .button {
       display: block;
     }
   }
 }
</style>

【modals/register.vue】
変更部分はlogin.vueと同じです

register.vue
<template>
 <div class="modal-container">
   <!-- 切り替える中身 -->
   <form
     v-if="$route.query.modal === 'register'"
     class="form"
     @submit.prevent="$emit('submitRegister', form)"
   >
     <label class="label">
       <span class="label">
         {{ $route.query.modal }}
       </span>
       <input
         v-model="form.email"
         :type="type"
         placeholder="email"
       >
     </label>
     <button
       class="button"
       type="submit"
     >
       {{ $route.query.modal }}
     </button>
   </form>
 </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
 props: {
   type: {
     type: String,
     default: 'text',
   },
 },
 data () {
   return {
     form: {
       email: '',
     },
   }
 },
})
</script>

<style lang="scss" scoped>
 .modal-container {
   .form {
     .label {
       font-size: 24px;

       .label {
         display: block;
       }
     }

     .button {
       display: block;
     }
   }
 }
</style>

【components/organisms/modals/ModalRoute.vue】

NidakRoute.vue
<template>
 <div
   v-if="$route.query.modal"
   class="modal-route"
 >
   <div
     class="bg"
     @click="$router.push('/')"
   />
   <div class="modal-wrap">
     <button
       class="button"
       @click="$router.push('/')"
     >
       <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z" /></svg>
     </button>
     <!-- <ModalContainer
       @submit="submit"
     /> -->
     <component
       :is="$route.query.modal"
       @submitLogin="submit($event)"
       @submitRegister="submit($event)"
     />
   </div>
 </div>
</template>

<script>
import Vue from 'vue'

export default Vue.extend({
 name: 'Modal',
 components: {
   login: () => import('@/modals/login.vue'),
   register: () => import('@/modals/register.vue'),
 },
 methods: {
   submit (form) {
     console.log(form) // eslint-disable-line
   },
 },
})
</script>

<style lang="scss" scoped>
 .modal-route {
   position: fixed;
   top: 0;
   width: 100%;
   height: 100%;

   .bg {
     width: 100%;
     height: 100%;
     background-color: rgba(0,0,0,0.5);
   }

   .modal-wrap {
     border-radius: 8px;
     background-color: #ffffff;
     width: 50%;
     height: 50%;
     position: absolute;
     top: 50%;
     left: 50%;
     transform: translate(-50%, -50%);
     padding: 30px;

     .button {
       border: none;
       position: absolute;
       top: 5%;
       right: 2%;
     }
   }
 }
</style>

【解説】
・component :is="$route.query.modal"
 └:isでcomponentを呼び出します!
  コンポーネントの登録が必要なので
  中身をそれぞれimportしましょう?
 └それぞれを1つのコンポーネントとみなし
  queryで切り替えましょう?
@submitLogin="submit($event)"
 └$emitのイベント名を変えたので
  どちらも書きましょう✍️
 └($event)は省略可能

お疲れ様でした?
完成です??

次回予告

【Nuxt.js】アプリ開発実践編:
Nuxt + Vuex + firebaseでログイン付きToDoリスト

こちらのTODOリストに
ログイン機能をつけていきます!✨
https://note.com/aliz/n/n8411db2c9a20

公開予定日は5/19(火)です?

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

Goole maps API でドラゴンボールの情報を快適にみよう!!

はじめに

  • ピンに情報を埋め込む方法
  • 他のピンをクリックした時
  • cssで装飾
  • 最後に

ピンに情報を埋め込む方法

ピンに情報を埋め込む方法は私の一個前の記事で紹介しています!
ぜひ参考にしてください!
https://qiita.com/Ryunosuke-watanabe/items/48c8ec80f87283cc0006

今回は前回立てたピンをクリックしたあとに、他のピンをクリックしたときの挙動を施します。
複数のピンが乱立していると見えにくいので、2個目のピンをクリックしたときに他のピンは消えるようにしましょう。

他のピンをクリックした時

htmlとcssは前回と同じです。

maps.html
<html>
<head>
    <meta charset="UTF-8">
    <title>map</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div id="map"></div>
    <script src="js/map.js"></script>
    <script src="data/place.json"></script>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></scriptsrc>></script>
</body>
</html>
style.css
body {
    height: 100%;
    width: 100%;
}

#map {
    height: 100%;
    width: 100%;
}

JavaScriptの方で操作していきます。今回はみやすいようにJSONのデータを配列として記載しておきます。
データのballoonのなかに紹介文を本来は記載しますが、ホゲホゲとしておきます。

map.js
var map;
var marker = [];
var infowindow = [];
var Center = {lat: 24.4064, lng: 124.1754};
var place_data = [
    {
        "loc": "バンナ公園",
        "lat": 24.375031,
        "lng": 124.160795,
        "balloon": "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge"
    },
      {
        "loc": "石垣島鍾乳洞",
        "lat": 24.361743,
        "lng": 124.154466,
        "balloon": "test"
    },
      {
        "loc": "石垣やいま村",
        "lat": 24.40489,
        "lng": 124.144636,
        "balloon": "test"
    }
]

function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
        center: Center,
        zoom: 11.5
    });
    markerset()
};

function markerset(){
    for(var i=0;i<place_data.length;i++) {
        marker[i] = new google.maps.Marker({
            position: {lat: place_data[i].lat, lng: place_data[i].lng},
            map: map,
            title: place_data[i].loc
        });

        infowindow[i] = new google.maps.InfoWindow({
            content: '<div class="mapsballoon"><h2>' + place_data[i].loc + '</h2>' + place_data[i].balloon + '</div>'
        });
        markerEvents(i)
    }
}

var opened = place_data.length + 1;
function markerEvents(i) {
    marker[i].addListener('click', function() {
        if (opened != place_data.length + 1) {
            infowindow[opened].close(map, marker[opened]);
        }
        infowindow[i].open(map, marker[i]);
        opened = i;
    });
}

openedという新しい変数を用意しておきます。そしてif文で2回目以降マーカーをクリックしたときに前回開いたマーカーを消すという作業をしています。これで二つ目のマーカーをクリックしたとき最初のマーカーは消えます。

image.png

ですが、紹介文があまりに長いとこのような感じでwindowが横長になってしまいます。なのでcssで変更を施していきたいと思います。

cssで装飾

実はJavaScriptでdivでballoonの中を囲ってあるので、class名のmapsballoonを指定して装飾することができます。

style.css
body {
    height: 100%;
    width: 100%;
}

#map {
    height: 100%;
    width: 100%;
}

.mapsballoon {
    width: 150px;
    height: auto;
}

横幅を150pxに指定、高さをautoにしておきました。そうすることで、横長くなることがなくなります。

image.png

こんな感じになりました!!

最後に

いかがだったでしょうか?前回の予告通りピンを立てる際の工夫を施しました。次回はgeocoding機能を実装したいと思います。

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

JavaScript の ?? と || でどっちが0の判定どうなるんだっけ

JavaScriptの ??|| ってどういう動きするのだっけ? といつも忘れるので書いておきます。あと記号のググラビリティも悪いので名前も記録しておきたい。

まとめ: falsyなものを判定するなら||でOK、nullまたはundefinedだけを判定するなら??の方が良い。

image.png

null合体演算子 ??

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

Null 合体演算子 (??) は論理演算子の一種です。この演算子は左辺が null または undefined の場合に右の値を返し、それ以外の場合に左の値を返します。

左辺が '' や 0 の場合は左の値を評価して返します

論理OR演算子 ||

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_Operators

true に変換できる値は、いわゆる truthy です。false に変換できる値は、いわゆる falsy です。

false と見ることができる式の例は、null、0、空文字列 ("")、あるいは、undefined と評価されるものです。

おまけ

ちなみにこういう演算子とかを検索するときに ?? とかで検索してもだいたい出てこないのでどうやって検索しているかという話。
?? の場合は javascript question double とか記号の名前と特徴を英単語で検索すると「これってなんなの?」的な質問サイトに行き当たって正式名称を知ることができます。

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

華麗なるGatsby.jsの実践(styled-components/headの編集/404ページ/画像の使用)

以前 gatsbyの公式チュートリアルを意訳しつつやったので、今回は、実際に業務で使用するにあたって
使いそうな機能について公式のドキュメント等を調べてみました。

スクリーンショット 2020-05-15 14.10.20.png

この記事のタイトルをつけた後に気づいたんですが、
公式で上記の画像を発見しました。
華麗なるギャツビーがやはり由来なんでしょうか。

お品書き

実際業務で使いそうな機能をピックアップしたところ、以下のようになりました。
- styled-componentsを使いたい
- head修正できるようにしたい
- 404ページをカスタマイズしたい
- gatsbyでの画像の扱いを知りたい

今回はhttps://www.gatsbyjs.org/starters/gatsbyjs/gatsby-starter-default/
このスターターを使って進めます。

スターターを使用するために、以下のコマンドを実行します。

$ gatsby new app https://github.com/gatsbyjs/gatsby-starter-default
$ cd app
$ gatsby develop

styled-componentsを使いたい

公式参考ページはこちら

必要なプラグインを取得します。

$ npm install --save gatsby-plugin-styled-components styled-components babel-plugin-styled-components

babel-plugin-styled-componentsはstyled-componentsをより扱いやすくしてくれるpluginです。

https://github.com/styled-components/babel-plugin-styled-components

gatsby-config.jsに以下を書き加えます。

gatsby-config.js
module.exports = {
  plugins: [`gatsby-plugin-styled-components`],
}

あとは使うだけ。思ったよりも簡単だった。

index.html
import styled from "styled-components"


const IndexPage = () => (
  <div>
    <Title>宇宙の日記</Title>
  </div>
)

export default IndexPage

const Title = styled.h1`
  color: blue;
`

head修正できるようにしたい

公式の参考ページはこちら
実は上記スターターにはすでに組み込まれていますが、念の為最初から実装方法を確認します。

gatsbyではreact helmetがサポートされているのでそれを使う。

npm install --save gatsby-plugin-react-helmet react-helmet

gatsby-config.jsに下記2つをを加える。

gatsby-config.js
{
  plugins: [`gatsby-plugin-react-helmet`]
}
gatsby-config.js
module.exports = {
  siteMetadata: {
    title: `色々な紙飛行機`,
    description: `多種多様な紙飛行機。その世界に触れてみませんか?`,
    author: `@irico`,
  },

下記のようなcomponentを準備して...

src/components/SEO
import React from "react"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"

function SEO() {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
          }
        }
      }
    `
  )

  return (
    <Helmet
      title={site.siteMetadata.title}
      meta={[
        {
          name: `description`,
          content: site.siteMetadata.description,
        },
      ]}
    />
  )
}

export default SEO

あとはpageで使うだけ!これも簡単。
ページごとにカスタマイズしたいのであれば、SEOのpropsとして渡して分岐させてあげればよし。

const IndexPage = () => {
        <Layout>
            <SEO />
            ...
        </Layout>
}

404ページをカスタマイズしたい

公式の参考ページはこちら

^\/?404\/?$ (/404/, /404, 404/ or 404)

上記の正規表現に該当するpageを作成すればいいだけ!

gatsby developコマンドでは下記の画像のようなページになりますが、 ビルド後はカスタマイズした404ページに飛ぶようになります。

実はこれも上記スターターで最初からページが用意されています(至れり尽くせり)

ローカルで404ページを確認したいときは、 Preview custom 404 pageを閲覧すればOK!
スクリーンショット 2020-05-13 18.19.35.png

gatsbyでの画像の扱いを知りたい

公式の参考サイトはこちら
普通のパス指定での読み方ももちろんできるが、webpackによるimportがオススメです。
ファイルをあたかもJSソースのように取得することができ、以下の恩恵が得られます。

  • webpackが圧縮をおこなってくれる
  • ユーザーに404エラーを出す前にコンパイルエラーが出る(チェック漏れを防ぐ)
  • ファイル名にハッシュが含まれるおかげで、ブラウザのキャッシュを防ぐ。
import kvImg from "../images/kv.jpg"

const IndexPage = () => (
  <Layout>
    <IndexWrapper>
       <img src={kvImg} alt="紙飛行機の画像" />
    </IndexWrapper>
  </Layout>
)

escape hatchあるよ!

アプリ下にstaticというファイルを作ってこのファイル内に画像などを置くと、publicフォルダー内にコピーされる。
そうすると呼び出さずに画像を使うことができる!

staticフォルダーにkv.jpgを配置したのち、

const IndexPage = () => (
  <Layout>
    <IndexWrapper>
       <img src='/kv.jpg' alt="紙飛行機の画像" />
    </IndexWrapper>
  </Layout>
)

勝手にコピーしてくれるので呼び出しは不要!

ただ、以下の短所がある。

  • ファイルが縮小されない
  • ユーザー側に404が表示される
  • コンテンツハッシュが含まれないため、キャッシュされてしまう

なので基本的にはJSを介してアセットを使うのがいいです。static folderが役に立つのは以下のような場合になります。

  • maifestなどの、特定のファイル名でなければならないもの

  • 画像がたくさんあり、パスを動的に参照する必要がある場合

  • Pace.jsのように、バンドルするコード外部に小規模のスクリプトを読み込みたい場合

  • webpackと互換性がないもの

etc..

gatsby-image

https://www.gatsbyjs.org/tutorial/gatsby-image-tutorial/#querying-data-for-a-single-image

  • Intersection Observer APIを使用した遅延読み込み
  • 画像の位置を保持することで、画像を読んだ途端ページ位置がずれることを防ぐ
  • 灰色の背景/ぼやけた画像などの設定が簡単にできる。

などなど、モダンな画像処理をしてくれるらしいです。

ただし、 imgタグの完璧な代替ではないので注意。適したものとしては、固定された大きさのイメージやコンテナ全体に大きく広がるイメージなど。

実は上記スターターでは、宇宙飛行士の絵でgatsby-imageが使用されています。(src/component/image.js)

npm install gatsby-transformer-sharp gatsby-plugin-sharp gatsby-image

どのスターターも用いてない場合は、下記もインストールする。スターターを使用している場合は最初から含まれているパッケージです。

npm install gatsby-source-filesystem

gatsby-config.jsを下記のように書き直す。

module.exports = {
  plugins: [
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/data/`,
      },
    },
  ],
}

GraphQLで画像データを扱えるようにするために,gatsby-sourcr-filesystemに画像があるフォルダーを教えています。

先にsrc/components/image.js設定部分を示します。

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

const Image = () => {
  const data = useStaticQuery(graphql`
    query {
      placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) {
        childImageSharp {
          fluid(maxWidth: 300) {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  `)

  return <Img fluid={data.placeholderImage.childImageSharp.fluid} />
}

export default Image

fileのパスは、先ほどgatsby-source-filesystemのconfigで設定したパスからの相対パスとなります。

まず知るべきなのは、gatsby-imageでは2つのresponsiveタイプがあるということです。

  • fixed

  • fluid

fixedは固定幅,fluidはコンテナに合わせて縮小拡大するタイプです。

上記のクエリを用いてサイズを指定します。

GraphQL fragments を使うことでイメージの設定を行うことができます。
GatsbyImageSharpFluidもそのフラグメントの1つ。
ただしこれは、GraphiQLでは使用できないので注意。

フラグメントについてはいまいち使い分け等がわからなかったので今後要調査....。

画像等の扱いについては上記4つの方法がありますが、使い分けについては

  • 直接パス指定 ・・・あまり使わなそう
  • gatsby-image ・・・基本的にはこれ!最適化等してくれるので楽
  • jsでのimport ・・・imgタグで色々カスタマイズして配置したい場合
  • static folder ・・・動的な読み込みや制約がある等

と感じました。実際に使っていく中で適宜使い分けたいと思います。

所感

実務で必要そうだな...と思った部分はほとんどスターターに最初から組み込まれていました。
しかも、使用したい機能についてはドキュメントが手厚く説明してくれてることが多かったです。
優秀ですね!
次回はwordpress等のプラグインやフォーム等がどこまで使えるのか?について調べてみたいと思います。
静的サイトジェネレータなので、動的な部分はほどほどがいいのかもしれませんが、その辺のパフォーマンス的な兼ね合いも調査しつつ...

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

爆速構築!json-serverでMock API

はじめに

フロントエンド開発のためにAPIのモックが必要になったので、json-serverを利用してモックサーバを構築していきます。

基本的な環境構築

初期化

powershell
# プロジェクトディレクトリを作成し, 移動する
mkdir mock-api
cd ./mock-api

# 初期化処理
npm init -y

json-server

今回のメインとなる json-server を導入します。

powershell
npm i -D json-server

contents.json

エンドポイントとなる json ファイルを作成します。

powershell
mkdir api
new-item api/contents.json

作成した contents.json を編集します。
ここで編集した内容がAPIのレスポンスとして返されるようになります。

contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ]
}

package.json

package.json を更新します。

package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから追加 ↓↓↓↓↓ ###
    "json-server": "json-server --watch ./api/contents.json --port 5000"
    ### ↑↑↑↑↑ ここまで追加 ↑↑↑↑↑ ###
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}

実行

npm run コマンドで実行します。

powershell
npm run json-server

実行されると、以下にGET通信することで先ほどのcontents.jsonの内容が得られます。

http://localhost:5000/contents

応用的な環境構築

エンドポイントを複数用意したい場合

エンドポイントを複数用意したい場合、contents.jsonにもう一つ要素を追加することで解決します。

contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ],
    "authors": [
        {
            "id": 1,
            "name": "hoge"
        },
        {
            "id": 2,
            "name": "fuga"
        }
    ]
}

上記のように編集し実行すると、以下のように通信先を切り替えることができます。

http://localhost:5000/contents
http://localhost:5000/authors

エンドポイント毎にファイルを分割したい場合

しかし、APIの規模が大きくなってくると単一ファイルではメンテナンスが難しくなってくることは想像に難しくありません。
そうなると「エンドポイント毎にファイルを分割したい」という欲求が生まれてきます。

そこで問題となってくるのが json-server の「単一ファイルしか受け付けない」という仕様です。
なので、今回は複数ファイルを一つにマージすることで対応したいと思います。

【参考】
  - Json-serverでモックAPI

(1) まず、マージをするためのスクリプトを用意します

powershell
mkdir scripts
new-item merge.js
scripts/merge.js
const path = require("path");
const fs = require("fs");

const root = path.resolve("./", "api");
const update = () => 
{
    const api = fs.readdirSync(root).reduce((api, file) => {
      if (api === undefined)
        api = {};

      if (path.extname(file) == ".json") {
        const endpoint = path.basename(file, path.extname(file));

        if (api[endpoint] === undefined)
          api[endpoint] = {};

        api[endpoint] = JSON.parse(fs.readFileSync(root + "/" + file, "utf-8"));
        return api;
      }
    }, {});

    fs.writeFile(root + "/../merged.json", JSON.stringify(api), err => {
      if (err)
          throw err;
    });
}

// 初回作成
update();

// jsonファイルを監視し, 監視ファイルに更新があるたびmerged.jsonを更新
fs.watch(root, (e, filename) => update());

(2) json-servermerge.js を両方同時に動作させるために npm-run-all を導入します

powershell
npm i -D npm-run-all

(3) package.json を更新します

package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから追加・更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000",
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
    ### ↑↑↑↑↑ ここまで追加・更新 ↑↑↑↑↑ ###
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}

(4) contents.jsoncontents.jsonauthors.json に分割します

powershell
new-item api/authors.json
contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ]
}
authors.json
{
    "authors": [
        {
            "id": 1,
            "name": "hoge"
        },
        {
            "id": 2,
            "name": "fuga"
        }
    ]
}

(5) 以下のコマンドで実行します

powershell
npm run serve

これで複数ファイルに分割定義することが可能となります。

POSTやPUTでもレスポンスを受け取りたい場合

json-serverGET以外 のリクエストだと思ったようにレスポンスを返してくれないので、処理をフックして GET通信 に偽装します。

【参考】
  - json-server で使い捨てモックサーバを作る
  - JSON Serverを使ってGETとPOSTでレスポンスを変えてみた

(1) まず、処理をフックするためのスクリプトを追加します

powershell
new-item ./scripts/middleware.js
middleware.js
module.exports = (req, res, next) =>
{
    if(req.method == 'POST') {
        req.method = 'GET'      // GETに偽装
        req.query = req.body
    }
    next()
}

(2) package.json を更新します

以下では、--middlewares オプションを追加しています。

package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000 --middlewares ./scripts/middleware.js",
    ### ↑↑↑↑↑ ここまで更新 ↑↑↑↑↑ ###
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}

これでPOST通信でもjsonレスポンスを得ることができます。

APIのrouteをカスタムしたい場合

現状、APIのrouteが http://localhost:5000/{jsonファイル名} となっているため、これを http://localhost:5000/api/v1/{jsonファイル名} となるように変更してみたいと思います。

【参考】
  - Json-serverでモックAPI

(1) まず、routes.json を追加します

powershell
new-item ./routes.json
routes.json
{
  "/api/v1/*": "/$1"
}

(2) package.json を更新します

以下では、--routes オプションを追加しています。

package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000 --routes ./routes.json --middlewares ./scripts/middleware.js",
    ### ↑↑↑↑↑ ここまで更新 ↑↑↑↑↑ ###
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}

これでhttp://localhost:5000/api/v1/{jsonファイル名}でアクセスできるようになります。

おわりに

さらに詳しくは 公式サイト をご参照ください。

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

JavaScript ページ内の文字列をすべて置換するコード

javascript:void((function(){var d=document;d.body.innerHTML=d.body.innerHTML.replace(/FROM_STRING/g,'TO_STRING');})());

参考

ページ内の文字列を置換するブックマークレット

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

【JavaScript】わたし的「スッキリJS記法」集

はじめに

「こんな書き方もできるんだ!便利!」と感じた記法。
随時更新予定!

プロパティを動的に生成する

プロパティ名をブラケットで括ることで、式の値から動的にプロパティ名を生成できる(Computed Propaty Names)。
※普通に書くとなぜか表示されないため、ブラケット部分コメントアウトしてます。

sample.js
 let i = 0;
 let todo = {
   month: new Date().getMonth(),
   date: new Date().getDate(),
   //['ToDo'+ ++i]: '買い物',  
   //['ToDo'+ ++i]: '片付け',
   //['ToDo'+ ++i]: '掃除'
 };

 console.log(todo);

↓ 結果
スクリーンショット 2020-05-15 10.51.57.png

オブジェクト内のメゾット定義省略記法

sample.js
 let member = {
   toString: function(){ //
    //..処理
   },
 }

 //省略記法
 let member = {
   toString(){
     //..処理
   },
 }

return文の省略

sample.js
const members = {
 name: "ハナコ"
};

//return文を省略したとき
const getName = (key) => members[key];

   // 複数行で書いたとき
   // const getName = (key) => {
   //   return members[key];
   // };

const membername = getName('name');
console.log(membername); // 結果:ハナコ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JS初心者道場_ オブジェクトについて①

JavaScriptのオブジェクトを案件で使用することはあるのですが「つまりどういう物だ」ということを答えろと言われると困ってしまうし、使ってはいるけれどどのような場面で利用するのが適切なのかが曖昧なので、しっかり利用できるように細部まで調べてみます。

オブジェクトを理解するとできるようになる(らしい)こと

  • グローバルオブジェクトの意味が理解できるようになる
  • メソッドを利用しているということがわかる

まず、オブジェクトとは

オブジェクトとは
「関連のあるデータと機能の集合です」
オブジェクトを使うことによって、データをまとめて扱えるようになります。

宣言方法は以下の通り、中括弧{}を使用します。

// 宣言方法
{}

// 型の確認
typeof {} // "object"

// Objectは変数に格納できる
const obj = {} // 例1
const person = {} // 例2

プロパティとメソッド

「関連のあるデータと機能の集合です」の機能とは大抵は変数と関数のことです。
オブジェクトのなかでは、変数のことをプロパティ、関数のことをメソッドと呼んでいます。

■プロパティ

オブジェクトが持つ変数(名前付きのデータ)のことを「プロパティ」と呼びます。
プロパティを設定するには、複数の宣言方法があります。

個人的には①を初期値として宣言するときに使ったりしていましたが、
③のやり方でデータをひとまとめにして引数にするみたいな使い方もできそうです。
②は、変数の数が初期で予想できない時に使えるのかな、と予想しかできません。

宣言方法①
const obj = {
  name: "taro",
  gender: "male",
  age: 20
}
宣言方法②
const obj = {}

obj.name = "taro";
obj.gender = "male";
obj.age = 20;
宣言方法③
const name = "taro";
const gender = "male";
const age = 20;

const obj = { name, gender, age }

メソッド

オブジェクトのプロパティには関数をセットすることもできます。
オブジェクトが持つ関数のことをメソッドと呼びます。

メソッドの宣言
const obj = {
  hello: function() {
    console.log('hello world')
  }
}

// 関数の実行
obj.hello(); // hello world

オブジェクトのプロパティを取り出す

オブジェクトのプロパティを取り出すには . (ドット) を使います。

アクセス方法
const obj = {
  name: "taro",
  gender: "male",
  age: 20
}

console.log(obj.name); // taro
console.log(obj.gender); // male
console.log(obj.age); // 20
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

耳年齢判定ボットを改良(LINEで音声ファイルを再生)

概要

普段は耳鼻科の開業医をしています。
以前obnizeのスピーカーからモスキート音を出し加齢性難聴をチェックするLINE Botを作成しました。
耳年齢を判定するLINE Bot×Iotの作成

今回、モスキート音をファイルに入れ、LINEで音声ファイルを再生できるようにしました。

作成方法

1.モスキート音を用意する

こちらを利用しました
Sine Tone Generator

『File Generator』の
『Hz』をモスキート音の周波数に設定、『duration』は3秒とし『DOWNROAD.WAV FILE』をクリックするとダウンロードできます。
各周波数分用意します。

image.png

ダウンロードしたファイルを再生するとサイン音が聞こえます。
image.png

2.publicフォルダを作ってwavファイルを設置

image.png

3.コードの追加

const config = {
    channelSecret: process.env.CHANNEL_SECRET,
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN
};

const app = express();
app.use(express.static('public'));  //追加

3.wavファイルのURLを動的に取得する
app.post('/webhook'......内の処理を書き換える

Promise
      .all(req.body.events.map(event=>handleEvent(event,req)))
      .then((result) => res.json(result));

function handleEvent(event) { に引数を追加

function handleEvent(event, req) {
  console.log(req);
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

メッセージ内にURLを入れる

let url;
  if (hz == 0) {
    url ="";
  } else if (hz == 8000) {
    url ="デプロイしたボットのURL/public/8000.wav";
  } else if (hz == 10000) {
    url ="デプロイしたボットのURL/10000.wav";
  } else if (hz == 12000) {
    url ="デプロイしたボットのURL/12000.wav";
  } else if (hz == 14000) {
    url ="デプロイしたボットのURL/14000.wav";
  } else if (hz == 15000) {
    url ="デプロイしたボットのURL/15000.wav";
  } else if (hz == 16000) {
    url ="デプロイしたボットのURL/16000.wav";
  }

LINEで複数のメッセージを返信する

 replyMessage(event.replyToken, [    
 { type: "text", text: "第一のメッセージ" },    
 { type: "text", text: '第二のメッセージ' }   
]) 

完成

image.png
image.png
image.png

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

Discord.jsでBotを作る【いろんな機能編その1】

この記事は、前回、前々回の記事の内容に準する記事になっています

前回、前々回の記事でBotの作り方、起動方法などを紹介しました。
今回は前々回の記事にもあった通り、Botにいろんな機能を作る方法、コードなどについて紹介します

まずは...

今度いろんなことに役立つjsonファイルについて紹介します。

jsonとは...

様々な言語で使える、テキストデータ保存に使えるファイルです(筆者何となくのイメージです。詳しくはググって())
まーつまり簡単なものの情報を保存しておけるもの、的な感じで覚えておけばいいと思います。
これはMySQLなどのデータベースを使ったデータ保存などの超絶初歩的な感じにもなるので、初心者にはお勧めです。

はい。話長いですね。じゃあさっそく使っていこー

Botにプレフィックスを追加する

プレフィックスがわからない人へ
Botで機能を実行する際、基本的に特定のメッセージを送信するという感じになりますが、たとえばただpingだけだと、他のBotなど被ってしまったり、日常会話などをしている最中に実行してしまったりと、いろいろと不便が生じます。
そこで、プレフィックスを言うものをつくり、!!ping(この!!の部分がプレフィックス)のようにすることで、他のBotとの干渉や誤動作を防げます。(肝心のプレフィックスがかぶってしまうと意味がないんですがね)

では、index.jsがあるフォルダにconfig.jsonというファイルを作りましょう。
中のコードは

config.json
{
    "prefix":"!!"
}

これは、prefixという名前で!!という内容の文字列を作った、という感じです。
ほかのことにも使えるので、覚えておくといいかも

そうしたらここに書いた内容をindex.jsで読み取れるようにしましょう。

index.jsconst discord ・・・と書いてあるところの近くに

indexjs
const config = require('./config.json')

これを追記しましょう。
これはconfigというのはconfig.jsonファイルの内容のことだよ、と定義してる感じです。
これでさきほど追加したprefixが使えるようになりました。
では使いましょう(?)

client.on('message', message =>{の中に

index.js
    if(message.content.indexOf(config.prefix) !== 0) return;
    const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
    const command = args.shift().toLowerCase();

を追加しましょう。
ここで注意なのが、前々回の記事に書いたような単語に単語する機能のコードの上にこれを書いてしまうと、動かなくなってしまします。前々回の記事の記事で言うと

inde.js
    if(message.content === "こんにちは"){
        message.reply("こんにちは!")
    }

この部分です。
なぜなら追加した分の一行目が、メッセージの中にプレフィックスがないのならばリターンする
というものだからです。
なのでこれを書く際には単語に反応するコードの下に書くようにしましょう

続いて二行目の解説です。二行目は引数を取得するコマンドです。
(例)!!help a b cこれのa、b、cの部分がそれぞれ引数
これは今後引数を代入するコマンドなどに使います。
三行目は、プレフィックスの後にあるメッセージ
つまり
コマンド名を取得するところです。これでもしcommandが○○だったらという分が書けるようになります

これでやっとプレフィックスの追加は終わり。いえー :tada:

実際に機能、コマンドを追加する

今回はpingコマンドとsayコマンドを追加します。
pingコマンドとは、Botの速度、つまり軽さをだすコマンドです。
sayコマンドは、Botに任意の文章を喋らせるコマンドです。
では作っていきましょう。

さっき書いたところの下に

index.js
    if(command === "ping"){
        message.channel.send(` Ping を確認しています...`)
        .then((pingcheck) => pingcheck.edit(`botの速度|${pingcheck.createdTimestamp - message.createdTimestamp} ms`))
    }

これをコピペしましょう。
多少の誤差は出ますが、これでBotの速さを測定できます。
見ためはのようになっています
キャプチャ.PNG
これでもうpingコマンドは完成です。

次はsayコマンドを作ります
先ほどのpingコマンドの下に

index.js
    if(command === "say"){
        const say_message = args.join(" ");
        message.delete().catch(msg=>{})
        message.channel.send(say_message);
    }

これを追加しましょう
これでsayコマンドの実装は終わりです!
使い方は<prefix>say (喋らせたい物)です
実際に使ってみるとこんな感じ
キャプチャ.PNG

おわり

前々回の記事の内容と今回の記事の内容のコード全体はこのようになります

index.js
const discord = require('discord.js');
const client = new discord.Client();
const config = require('./config.json')


client.on('ready', () => {
    console.log('bot ready!');
});

client.on('message', async message =>{

    if(message.content === "こんにちは"){
        message.reply("こんにちは!")
    }

    if(message.content.indexOf(config.prefix) !== 0) return;
    const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
    const command = args.shift().toLowerCase();

    if(command === "ping"){
        message.channel.send(` Ping を確認しています...`)
        .then((pingcheck) => pingcheck.edit(`botの速度|${pingcheck.createdTimestamp - message.createdTimestamp} ms`))
    }

    if(command === "say"){
        const say_message = args.join(" ");
        message.delete().catch(msg=>{})
        message.channel.send(say_message);
    }


})

client.login('TOKEN');
config.json
{
    "prefix":"!!"
}

この記事を読んでくださいましてありがとうございました!
次はさらにいろんな機能を追加させる方法を書いておこうと思います!

(もしかしたら抜けてるところとか間違えてるところがあるかもしれません。もしも間違えがあったらコメントなどで指摘してください)

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

誰でもわかるJavascript - bind編

Javascriptのbindの記事を読んでも、さっぱりわからないという人(自分)のために解説してみる。

bindの基本

以下のオブジェクトが定義されているとする。

const person = {
  age: 20,
  displayAge: function() { console.log(this.age); }
};

personオブジェクトを通してdisplayAge関数を実行すると「20」が表示される。

person.displayAge(); // 20

displayAge関数を変数に代入して、実行すると何故か「undefined」が表示されてしまう。

const newDisplayAge = person.displayAge;
newDisplayAge(); // undefined

newDisplayAgeの中身を見てみると当然displayAge関数が代入されている。
しかし、thisが何であるかわからない状態になっている。1

this?
console.log(newDisplayAge); // function() { console.log(this.age); } 

そこでbind関数を使うとthisが何であるかを教えてあげることで正常に動作する。

const bindDisplayAge = person.displayAge.bind(person);
bindDisplayAge(); // 20

bindの応用

bindの第1引数にはthis、それ以降の引数には関数に渡す引数が定義できる。
以下のコードではgreet関数にthisが使用されていない。
そのため、第1引数にはnull渡し、第2引数でgreet関数のname引数に'taro'を渡している。2

const person = {
  greet: function(name) { console.log('hello ' + name); }
};

const greetPerson = person.greet.bind(null, 'taro');
greetPerson(); // hello taro

どういう時に使うのか

関数を実行したくないが、呼び出すだけで実行できる状態にしておきたいときに使う。
具体的な例としてボタンが押された時のイベントハンドラーにthisや引数が使用されている場合、bindが必要になる。

const person = {
  age: 20,
  displayAge: function() { console.log(this.age); }
};
const button = document.querySelector('button');

// 以下は想定の動作とは異なる。
button.addEventListener('click', person.displayAge()); // ページ読み込み後に'20'が表示
button.addEventListener('click', person.displayAge);   // ボタンクリック時に'undefined'が表示

// 正常に動作する。
button.addEventListener('click', person.displayAge.bind(person)); // ボタンクリック時に'20'が表示

  1. 正確に書くとブラウザで実行した場合、thisはWindowオブジェクト 

  2. 第1引数にpersonを渡してもエラーにはならない 

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

Deno 1.0.0 と Servest 1.0.0 で HelloWorld

Deno

Deno https://deno.land/

asdf

https://asdf-vm.com/#/

  • いろいろパッケージマネージャ
  • プラグインを入れることでいろんなパッケージを入れることができる

インストール

git で ~/.asdf に落としてきて最新化

git clone https://github.com/asdf-vm/asdf.git ~/.asdf
cd ~/.asdf
git checkout "$(git describe --abbrev=0 --tags)"

.bash_profile.zshrc に以下を追加してシェルを再起動

. $HOME/.asdf/asdf.sh

Deno のパッケージを入れる

asdfを使用してDenoのバージョンを管理する 必見

asdf plugin-add deno https://github.com/asdf-community/asdf-deno.git
asdf install deno 1.0.0
asdf global deno 1.0.0

Deno Helloworld

公式サンプルそのままで、見た目普通の js に見えるけど、①package.json とか作らなくていいし、②npm は使わないし (標準ライブラリを https で読んでる)、③awaitasync で囲わなくていいみたいなのが読み取れる。

index.js
import { serve } from "https://deno.land/std@0.50.0/http/server.ts";

for await (const req of serve({ port: 8000 })) {
  req.respond({ body: "Hello World\n" });
}

実行は deno run hoge。今時のセキュリティも考慮しているのでネットワークアクセスにはフラグが必要。

deno run --allow-net index.js

Deno Helloworld

interpreter

インタープリタもあるよ

% deno
Deno 1.0.0
exit using ctrl+d or close()
> console.log('Yo')
Yo
undefined
> const yo = 'Yo'
undefined
> yo
Yo
> 

Servest で動かす (React/tsx)

Servest は Deno 向けの http サーバー。
https://servestjs.org/
作者:https://keroxp.me/
Deno 1.0.0 に追従するように Servest も 1.0.0 に対応した。?

これも Servest の公式サンプルそのままですみません。
React/tsx が普通に動く。
パッケージは https 経由で指定できるので npm install 不要 (node_modules が作られない)。
Router とかも実装されているので実践で使えてしまえそう。

index.tsx
// @deno-types="https://servestjs.org/@v1.0.0/types/react/index.d.ts"
import React from "https://dev.jspm.io/react/index.js";
// @deno-types="https://servestjs.org/@v1.0.0/types/react-dom/server/index.d.ts"
import ReactDOMServer from "https://dev.jspm.io/react-dom/server.js";
import { createApp } from "https://servestjs.org/@v1.0.0/mod.ts";

const app = createApp();
app.handle("/", async (req) => {
  await req.respond({
    status: 200,
    headers: new Headers({
      "content-type": "text/html; charset=UTF-8",
    }),
    body: ReactDOMServer.renderToString(
      <html>
        <head>
          <meta charSet="utf-8" />
          <title>servest</title>
        </head>
        <body>Hello Servest!</body>
      </html>,
    ),
  });
});
app.listen({ port: 8899 });

そのまま実行

deno run --allow-net index.tsx

スクリーンショット 2020-05-15 8.19.59.png

かんそう

  • JS/TS エンジニアは Node.js からドンドコ移行しそう
  • マスコットかわいいので流行りそう
    • チンアナゴかと思ってた
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

お絵かきできるSNSを作りたい!7

線の太さを実装していきたいと思います。

↓この部分
WS000002.JPG

前回同様、今度は[type="range"]の値を取得し、lineWidthに入れるだけです。

<input type="range" value="1" min="1" max="10" step="1" oninput="LineWidth(this)">
<output id="LineWidthValue">1</output>pixel
//線の太さ用変数(デフォルトは1px)
var strLineWidth="1";

function LineWidth(obj){
    document.getElementById('LineWidthValue').value=obj.value;
    strLineWidth=obj.value;
}

最後に

    ct.lineWidth=strLineWidth;

↑ここだけだと伝わらないと思うので↓のgithubの差分をを参照ください。

[github]今回の修正内容はこちら

これで線の太さ変更が可能になりました。
WS000003.JPG

最初:お絵かきできるSNSを作りたい!

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

お絵かきできるSNSを作りたい!6

カラーパレッドを実装していきたいと思います。

↓この部分
WS000000.JPG

このままでも良いのですが、好きな色を追加出来る用にもした方がいい気がしてきたのでその他というパレットも用意します。

その他:<input type="color" id="color_other" name="color" onChange="Color(this);">

onchageイベントを付与しました。
JavaScriptやjQueryでバインドしても良いのですが、それだと後からどの要素にイベントが入っていたか分かりにくいので直接書く派です。

↓ということで、1番下にその他を追加。
WS000001.JPG

前回作った色の四角形を選択した時に枠線を付けているColorという関数があったので、それを改造します。

//カラー設定用変数(デフォルトは黒)
var strStrokeStyle="#000000";

//カラー選択関数
function Color(obj){
    for ( i = 1; i <= 15; i++ ) {
        id_name = "color_" + i;
        if (id_name == obj.id){
            id_name2 = "#" + id_name;
            document.getElementById(id_name).classList.add('active');
        } else {
            document.getElementById(id_name).classList.remove('active');
        }
    }
    if (obj.id == "color_other"){
        strStrokeStyle = $("#color_other").val();
    } else {
        strStrokeStyle = "#" + $(id_name2).css("background-color").match(/\d+/g).map(function(a){return ("0" + parseInt(a).toString(16)).slice(-2)}).join("");
    }
}

追加したのはid_name2変数の追加と↓以下の部分です。

    if (obj.id == "color_other"){
        strStrokeStyle = $("#color_other").val();
    } else {
        strStrokeStyle = "#" + $(id_name2).css("background-color").match(/\d+/g).map(function(a){return ("0" + parseInt(a).toString(16)).slice(-2)}).join("");
    }

その他を押した時は、タグのidが”color_other”なので[type="color"]の値を取得・それ以外の場合は選択した四角形の背景色を取得しています。

type="color"は、そのままだと10進数のRGB表記だったので、上と揃えようと思い16進数表記への変換も行っています。

ここに来てjQueryが無い事に気が付き追加しました。
3系で問題無いのですが、古参なのでIEも考慮し1系で。
color関数の上のfor文はjQueryなら数行で収まるはずですが、この程度のループなら速度的にも影響ないかなそのままにしました。

そして、最後にグローバルなstrStrokeStyle変数に格納した情報をcanvasのgetContextでstrokeStyleが取得出来る用にします。

    ct.strokeStyle=strStrokeStyle;

かくして、色の変更が可能になりました。
WS000002.JPG

[github]今回の修正内容はこちら

次:お絵かきできるSNSを作りたい!7
最初:お絵かきできるSNSを作りたい!

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

ページ遷移はどうするの?

ページ遷移ってどうするの?

あれ?ルーターがなくない?

今までReactを書いていてそろそろサーバーサイドレンダリングに手を出した私、よしポートフォリオサイトを作ってやろ!と考えたわけですよ!ここである問題が発生!あれ、Reactのときはページ切り替えはruterを使ってたけど...NextってRuterないやんけどうするんや〜!
いや安心してくださいませ〜
別にないわけではないです。
NextではRuterを使わなくてもページ繊維なんてちょちょいのちょいで行えるのです!
(あ!ReactでRuterを使うときは npm install ruterでルーターが使えるようにしてimportしてからでないと 使えませんので〜)npmとかのコマンドはまたどこかで説明するとして、話が逸れてしまったので本題に行ってみましょう!

じゃあ実際にどうすればいいの?

話は意外と簡単です!
Nextのアプリケーションは基本的にpageディレクトリ内に様々なファイルを記述しますが、そこにある.jsファイル、つまりページコンポーネントに import Link form 'next/link'と記述しましょう。これでNextで作成した各ページのjsファイルを遷移できるようになりました。あとはリンクを配置します。ページコンポーネントに以下のように記述します。

~.js
export default () => (
  <Layout header ="Top page" title ="Top page.">
    <p>Welcome to Next.js</p>
    <Link href ="./other">
      <button>Go to Other &gt;&gt;</button>
    </Link>
  </Layout>
);

とりあえず現在のページタイトルとかはtoppageとかにしといて遷移先のページのjsファイルの名前をother.jsとすると<Link href = "./other"> </Link>と言うように記述することでハイパーリンクが完成し、画面に表示されるようになります。つまり...

<Link href = " 遷移先のjsファイルの名前(.jsはいらない) ">
ここにボタンやテキストを入力する (ex<p>,<h3>,<button>,もしくは生テキスト....
</Link>

以上!これでNextにおける画面遷移はご理解いただけたでしょうか?
あ!忘れていました!今回のトップページはindex.jsとしていますが、もし遷移先にother.jsから戻るための画面遷移がしたいと思えばohter.jsの方でが上記のようなもの書きます。しかし、この時に遷移先がトップページであれば、href = "./index"と記述せずhref = "/"で行うことができます!覚えておきましょう!

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

Google maps APIでピンにドラゴンボールの情報を埋め込んでみよう!

はじめに

  • 複数のピンの立て方のおさらい
  • 情報を付け加える方法1
  • 情報を付け加える方法2
  • 最後に

複数のピンの立て方のおさらい

私の一個前の記事に複数のピンの立て方が掲載されていますので、そちらを参照してください。
Google maps APIで複数のドラゴンボールの場所にピンを立てよう! - Qiita

今回は立てたピンに情報を付け加えていきたいと思います。前回同様、複数のピンを目標としますのでfor文をうまく活用していきたいと思います。

Htmlは前回のままです。

maps.html
<html>
<head>
    <meta charset=“UTF-8”>
    <title>map</title>
    <link rel=“stylesheet” href=“css/style.css”>
</head>
<body>
    <div id=“map”></div>
    <script src=“js/map.js”></script>
    <script src=“data/place.json”></script>
    <script src=“https://maps.googleapis.com/maps/api/js?key=AIzaSyBZUNbx_85VM6LurRVIHZcxl-7Vg3O2C9g&callback=initMap” async defer></scriptsrc>></script>
</body>
</html>

Cssも前回と同様です。

style.css
body {
    height: 100%;
    width: 100%;
}

#map {
    height: 100%;
    width: 100%;
}

情報を付け加える方法1

ここまでは前回と同じですが、JSONの方から改善していきたいと思います。JSONにその場所の紹介文を載せておきたいと思います。今回は全てtestとしておきます。

place.json
var place_data = [
    {
        “Loc”: “バンナ公園”,
        “lat”: 24.375031,
        “lng”: 124.160795,
        “balloon”: “test”
    },
      {
        “Loc”: “石垣島鍾乳洞”,
        “lat”: 24.361743,
        “lng”: 124.154466,
        “balloon”: “test”
    },
      {
        “Loc”: “石垣やいま村”,
        “lat”: 24.40489,
        “lng”: 124.144636,
        “balloon”: “test”
    }
]

情報を付け加える方法2

次にJavaScriptの変更です、前回とは異なりmarkerの情報を保存するリストとウィンドウの情報を保存するリスト二つを用意します。

map.js
var map;
var marker = [];
var infowindow = [];
var Center = {lat: 24.4064, lng: 124.1754};
function initMap() {
    map = new google.maps.Map(document.getElementById(map), {
        center: Center,
        zoom: 11.5
    });
    markerset()
};

function markerset(){
    for(var i=0;i<place_data.length;i++) {
        marker[I] = new google.maps.Marker({
            position: {lat: place_data[I].lat, lng: place_data[I].lng},
            map: map,
            title: place_data[I].loc
        });

        infowindow[I] = new google.maps.InfoWindow({
            content: <div class=mapsballoon><h2> + place_data[I].loc + </h2>’ + place_data[I].balloon + ‘</div>
        });
        markerEvents(i)
    }
}

function markerEvents(i) {
    marker[I].addListener(click, function() {
        infowindow[I].open(map, marker[I]);
    });
}

このように書くことで、for文でmarker・infowindowにそれぞれ値を格納していくことができます。addListenerメソッドをしようすることで、クリックしたときにinfowindowが表示されるようにすることができます。

image.png

最後に

いかがだったでしょうか?前回の予告通りピンに情報を付け加える方法を紹介しました。次回はピンを表示する際の工夫を施していこうと思います。
参考文献
https://developers.google.com/maps/documentation/javascript/examples/event-simple

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

Google maps APIで複数のドラゴンボールの場所にピンを立てよう!

はじめに

  • ピンの立て方のおさらい
  • for文で回してピンを立てる
  • JSONからデータを取得する
  • 最後に

ピンの立て方のおさらい

私の一個前の記事にピンの立て方までが乗っていますので、そちらを参照してください!
Google maps APIでドラゴンボールのある場所にピンを立てよう!! - Qiita

今回は複数のピンの立て方について解説していきます。複数のピンを立てる際に、一個ずつ立てるのもいいですがそれだと効率が悪いです。ですのでfor文をしようして効率よくピンを立てていきたいと思います。

HTMLは前回のままです。

maps.html
<html>
<head>
    <meta charset=“UTF-8”>
    <title>map</title>
    <link rel=“stylesheet” href=“css/style.css”>
</head>
<body>
    <div id=“map”></div>
    <script src=“js/map.js”></script>
    <script src=“https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap” async defer></scriptsrc>></script>
</body>
</html>

Cssも前回と同じです。

style.css
body {
    height: 100%;
    width: 100%;
}

#map {
    height: 100%;
    width: 100%;
}

for文で回してピンを立てる

今回はJavaScriptでピンを複数立てる操作をしていきます。

map.js
var map;
var marker;
var place_data = [
    {
        Loc: “バンナ公園”,
        lat: 24.375031,
        lng: 124.160795,
    },
      {
        Loc: “石垣島鍾乳洞”,
        lat: 24.361743,
        lng: 124.154466,
    },
      {
        Loc: “石垣やいま村”,
        lat: 24.40489,
        lng: 124.144636,
    }
]
var Center = {lat: 24.4064, lng: 124.1754};
function initMap() {
    map = new google.maps.Map(document.getElementById(map), {
        center: Center,
        zoom: 11.5
    });
    marker()
};

function marker(){
    for(var I=0;i<place_data.length;i++) {
        marker = new google.maps.Marker({
            position: {lat: place_data[I].lat, lng: place_data[I].lng},
            map: map,
            title: place_data[I].loc
        });
    }
}

Markerというファンクションを作成しその中でfor文で回します。place_dataという配列を作成しその中に位置情報・場所の名前を記入しておきます。あとはfor文でmarkerを立てていくだけです。

JSONからデータを取得する

先ほどはJavaScriptの配列の中に位置情報や場所の名前を入れていましたが、今後のことを考えてjson の方にデータを移してJavaScriptとつなげたいと思います。

place.json
var place_data = [
    {
        “Loc”: “バンナ公園”,
        “lat”: 24.375031,
        “lng”: 124.160795,
    },
      {
        “Loc”: “石垣島鍾乳洞”,
        “lat”: 24.361743,
        “lng”: 124.154466,
    },
      {
        “Loc”: “石垣やいま村”,
        “lat”: 24.40489,
        “lng”: 124.144636,
    }
]

JSONの方でこのように書き込むとJavaScriptでスムーズにJSONと接続することができます。あとはhtmlでscriptを追加するだけです。

maps.html
<html>
<head>
    <meta charset=“UTF-8”>
    <title>map</title>
    <link rel=“stylesheet” href=“css/style.css”>
</head>
<body>
    <div id=“map”></div>
    <script src=“js/map.js”></script>
    <script src=“data/place.json”></script>
    <script src=“https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYg&callback=initMap” async defer></scriptsrc>></script>
</body>
</html>

これだけで簡単にJSONと接続することができます。これからたくさんのピンを立てたいと考えたときは、やはりJSONでデータを分けた方がいいと思いますのでこちらで書いたほうがいいでしょう。

image.png

最後に

いかがだったでしょうか?前回の予告通り複数のピンを立てる方法を紹介しました!次回はこのピンに情報を埋め込む方法を紹介したいと思います!

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

JavaScriptで空の配列を判別する方法

個人的にすごく詰まったので、メモとして残しておく。

JavaScriptでは条件式の中に比較演算子がない場合、要素によって真偽判定が行われるが、文字列などとは異なり、配列では空の場合でもTrueと判定されてしまう。

script
const name_string = ""  //空の文字列
console.log(Boolean(name_string))  //False

const name_list_1 = []  //空の配列
const name_list_2 = [1] //要素のある配列

//配列の要素の有無にかかわらずTrueを返してしまう
console.log(Boolean(name_list_1)) // True
console.log(Boolean(name_list_2)) // True

そのため、配列に要素が入っているか判定する場合には、配列の長さを求める.lengthを用いて行う。

script
const name_list_1 = []  //空の配列
const name_list_2 = [1] //要素のある配列

//配列が空のときFalseを返すようになった
console.log(Boolean(name_list_1.length)) // False
console.log(Boolean(name_list_2.length)) // True

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

javascriptとjqueryって何が違うの?

javascriptとjquery

プログラミングスクールで学んだ事を思い返しているとふと疑問が起こった。

「javascriptとjqueryって何が違うんだ?」

学んだ当初はよく理解出来ていないまま、がむしゃらにコードを書いていたのもあって僕の中で

jquery = javascriptのもう1つの名称

という何ともいい加減な式を頭に思い浮かべたまま卒業まで行ってしまった。

さすがに知識を曖昧なままにしてはいけないと思ったので簡単に説明していく。

まだ未熟なので説明が間違っているかもしれないがご容赦願いたい。

jquery = javascriptのライブラリ

そもそもライブラリって何だという話だが

ライブラリとは汎用性の高い複数のプログラムを再利用可能な形でひとまとまりにしたものであり

簡単に言うと「jqueryとはjavascriptで開発を行うのを簡単にするものである」

もっと簡単に言えば「jqueryとはjavascriptでよく使う機能や効果のあるコードを簡潔に再編成したものである」

どういうことかと言えばjavascriptで10行くらいコードを記述しなければいけない時に

jqueryを使えば1,2行に短縮できたりする。

つまりとても簡単で便利にjavascriptを使えるのである。

注意点

いくら便利だと言ってもjqueryにも弱点はある

1.読み込みが遅くなることがある

jqueryは複数のブラウザで使えるものだが、それゆえいくつものブラウザ判定や分岐が行われている。

つまり本来必要のない処理まで実行してしまっているのでその分動作が重くなってしまうのである。

2.jquery独自の書き方がある

javascriptを簡潔に書き直している形なのでjquery独自の命令文があり

javascriptを使うときにjqueryの記述を行うとエラーが出てしまう事がある。

それでも便利なjquery

とはいえjqueryは記述が簡単になるのでコードを書くときも見直すときも負担が少なくなり大変便利でなものである。

最初からjqueryを勉強するのもいいがjavascriptを勉強してからの方がもっと体系的に理解できるそうだ。

好きな勉強方法でjqueryを使いこなせるようになろう!

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

Google maps APIでドラゴンボールのある場所にピンを立てよう!!

はじめに目次

  • Google maps APIの取得
  • まずは地図を表示しよう!!
  • ピンを立ててみよう!
  • 最後に

Google maps APIの取得

まず取得するには、Googleアカウントとクレジットカードが必要になります。
詳しくはここをご覧になってください。
https://qiita.com/Haruka-Ogawa/items/997401a2edcd20e61037

まずは地図を表示してみよう!!

APIを取得したら、早速地図を表示してみましょう!
地図を表示するには、HTML,CSS,JavaScriptが必要になります。一応全てをHTMLで書くことができますが、
今後のことを考えて分けて書くようにします。まずはHTMLです。

map.html
<html>
<head>
    <meta charset="UTF-8">
    <title>map</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div id="map"></div>
    <script src="js/map.js"></script>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>
</body>
</html>

YOUR_API_KEYのところをあなたが取得したAPIに置き換えましょう!次はJavaScriptです。
今回は私が石垣島出身なので地図の中心の座標は石垣島にしたいと思います。

map.js
var map;
var Center = {lat: 24.4064, lng: 124.1754};
function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
        center: Center,
        zoom: 11.5
    });
}

JavaScriptで表示するマップの中心座標や拡大サイズなどを変更します。今回はその二つのみ指定していますが、それ以外にもピンを立てる場所やピンに記載する情報、経路の表示などを設定することができます。
次は、cssでmapのサイズを決めていきます。

style.css
body {
    height: 100%;
    width: 100%;
}

#map {
    height: 100%;
    width: 100%;
}

今回は画面全体をマップにするので縦・横共に100%ととしています。
これでマップを表示する準備が整いました。早速htmlを開いてみましょう!
image.png

このような感じで表示することができます!

ピンを立ててみよう!

地図が表示できましたがこれではまだ面白くないので、手始めにピンを立ててみましょう。

map.js
var map;
var marker;
var Center = {lat: 24.4064, lng: 124.1754};
function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
        center: Center,
        zoom: 11.5
    });
    marker = new google.maps.Marker({
        position: Center,
        map: map,
        title: "石垣島"
    });
};

ピンを立てるにはMarkerメソッドを使います。position,地図を表示する場所,markerのタイトルを決めます。今回は中心と同じ座標を使っています。表示してみるとこんな感じです。

image.png

無事ピンを立てることができました!

最後に

いかがだったでしょうか?私自身初めて地図を表示するときは何度もうまくいかず、あきらめかけたものです。そう言った人が少しでも減るようにこの記事を書きました。また、複数のピンを立てたり、経路の表示の仕方は追々書いていきたいと思います。
参考文献
https://developers.google.com/maps/documentation/javascript/examples/event-simple

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

洋数字が1桁の時は全角、2桁以上の時は半角にするツール

初めに

会社の文章のポリシーで洋数字が1桁の時は全角、2桁以上の時は半角にするというルールがありました。
いちいち手作業で直していると時間の無駄なのでツールを作りました。

簡単な仕様

  • 洋数字が1桁の時は全角、2桁以上の時は半角
  • 数字に挟まれた, . : / は半角に変換
  • 数字の間に, . : /があって2桁以上なら半角
    • (例)1,000、5/5、1:30 などは半角

簡単な設計

  • 最初に数字を全て半角にする
  • 1桁の数字を全角に変換
    • 前後の数字だけでなく , . : / にも注意する
    • 文頭は改行前の値を気にしない
    • 文末も次の段落の頭の文字を気にしない
    • etc...

ソースコード(javaScript)

初めに変数、hanに半角洋数字を、zenに全角洋数字を代入

var han = "0123456789";
var zen = "0123456789";

フォームから送られてきた値を変数textに挿入

  var text = document.henkan.mytext.value;

全ての数字を一度半角に変換

for文で、変数textの文字数処理を行う

  • text.charAt(i)で文字を取得
  • 上で取得した文字を、indexOfで変数zenに格納されている全角数字があるか探す。
  • あればn = zen.indexOf(c,0);で変数zenの何番目の文字に該当するか調べ変数nに代入、なければ0を代入
  • if (n >= 0)で検索しても、あれば、han.charAt(n);で変数hanの何番目かを調べ、それに変換し、変数cに代入。
  • 変数cの値を変数strに入れていく
  var str = "";
  for (i=0; i<text.length; i++){
    c = text.charAt(i);
    n = zen.indexOf(c,0);
    if (n >= 0){
      c = han.charAt(n);
      str += c;
    }else{
      str += c;
    }
  }

数字に挟まれた, . : / は半角に変換

※以下の(\d+?)●(\d+?)の●の部分は全角です

str = str
  .replace(/(\d+?)/(\d+?)/g, "$1"+"/"+"$2")
  .replace(/(\d+?).(\d+?)/g, "$1"+"."+"$2")
  .replace(/(\d+?),(\d+?)/g, "$1"+","+"$2")
  .replace(/(\d+?):(\d+?)/g, "$1"+":"+"$2");

1桁の数字は全角に

※以下のvar CONV_TABLE = ["0","1","2","3","4","5","6","7","8","9"];の洋数字は全角

str = str.replace(/(^|(?:[^0-9.,:/]))([0-9])((?![0-9.,:/])|$)/g,
  function(m) {
    var CONV_TABLE = ["0","1","2","3","4","5","6","7","8","9"];
    var s, n;
    if (m.length == 1) {
      s = "";
      n = CONV_TABLE[parseInt(m)];
    } else {
      s = m.charAt(0);
      n = CONV_TABLE[parseInt(m.charAt(1))];
    }
    return s + n;
  });

ユーザーが視覚的に数字の全角半角を確認できるようにする

- 変数str_chkを作り、ユーザーが確認できるように一桁洋数字には赤、2桁(記号含む)以上には青色で画面に返す

※以下の2行目のstr_chk = str_chk.replace(/([0-9]{1})/g, "<span class=\"bkred\">"+"$1"+"</span>");の洋数字は全角です

  str_chk = str
  str_chk = str_chk.replace(/([0-9]{1})/g, "<span class=\"bkred\">"+"$1"+"</span>");
  str_chk = str_chk.replace(/([0-9]+)([.,:/]{1})([0-9]+)([.,:/]{1})([0-9]+)/g, "<span class=\"bkblue\">"+"$1$2$3$4$5"+"</span>");
  str_chk = str_chk.replace(/([0-9]+)([.,:/]{1})([0-9]+)/g, "<span class=\"bkblue\">"+"$1$2$3"+"</span>");
  str_chk = str_chk.replace(/(\d{2,})/g, "<span class=\"bkblue\">"+"$1"+"</span>");
  var text = new String;
  text = '<div style="font-weight:bold;">■<span style="color:red;">1桁</span>の洋数字は<span style="color:red;">赤</span> <span style="color:blue;">2桁以上</span>は<span style="color:blue;">青</span>で表示されます。</div>';
  document.henkan.mytext.value = str;
  str_chk = str_chk.replace(/\r\n|\n/g, '<br>');
  document.getElementById('preview').innerHTML = str_chk;
  document.getElementById('kome').innerHTML = "↓ココをコピーして使って下さい" ;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

json api形式のスネークケースのレスポンスをキャメルケースにする[jsona]

スネークケースのレスポンスをキャメルケースにしたい

背景

フロントエンド側でapiつなぎこみ作業していて、
サーバーからのレスポンスがスネークケースで、
そのままフロント側で使うとeslintエラーになる。。。
そこでスネークケースをキャメルケースに手動で変換する。。。 ← これがめんどい、なんとかしたい

対処

こんな感じのレスポンスが来たとする

{
  "data": {
    "id": "1",
    "type": "movie",
    "attributes": {
      "name": "test movie",
      "year": null,
      "is_released": true,
      "director_name": "hoge"
    }
  }
}

まず、jsonaというライブラリを使って、扱いやすい形にする

import axios from 'axios'
import Jsona from 'jsona'

const dataFormatter = new Jsona()
const getData = async () => {
    try {
        const response = await axios.get('/data')
        console.log(dataFormatter.deserialize(response))
    } catch (error) {
        console.log(error)
    }
}

// console.logの結果
{
  type: 'movie',
  id: '1',
  name: 'test movie',
  year: null,
  is_released: true, // ここと
  director_name: 'hoge' // ここをキャメルケースにしたい
}

jsonaのオプションにいい感じのがある
jsonaの初期化時にオプションとして、SwitchCaseJsonMapperを下のように渡せば良い

import Jsona, { SwitchCaseJsonMapper } from 'jsona

const dataFormatter = new Jsona({
    jsonPropertiesMapper: new SwitchCaseJsonMapper({
        switchChar: '_'
    })
})

こうすることで先ほどのconsole.logの結果が、下のようにキャメルケースになる

{
  type: 'movie',
  id: '1',
  name: 'test movie',
  year: null,
  isReleased: true,
  directorName: 'hoge'
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む