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

【JavaScript】Promiseについて簡単に説明した

Promiseとは

要は非同期処理の完了を待つ為の仕組みのことです。

非同期処理とは

例えば、仕事中に上司から「これ1000枚コピーしといて」と言われたら、
コピー機を使いコピー機が勝手に1000枚作っている間は他の仕事を行い、
コピーが終わったところで1000枚の用紙を上司に渡すと思います。

これが非同期処理です

もし会社のコピー機が安物で一枚一枚手動でコピーするタイプのものだったら、1000枚できるまで他の仕事はできません。

これが同期処理です


非同期処理の方が便利で良いじゃんと思いきや、
これをコードで表すと不都合が起こります。

非同期処理であるsetInterval()を使って見てみます。

main.js
const copy = () => {
  const id = setInterval(() => {
    console.log(`${paper} 枚目`);
    if (paper >= 10) {
      clearInterval(id);
    }
    paper++;
  }, 100);
};

console.log('上司:これ10枚コピーしといて');
copy();
console.log(`コピー${paper}枚できました!`);

このコードでは元の用紙paperを用意し、
上司からの指示があった後、copy関数(コピー機)を実行。
setInterval()paperが10になるまで繰り返し、
10になったら上司に提出する。

という処理を行いたいものとします。

しかし結果を見てみると・・・
demo

これはマズイです。
コピー機の完了を待たずに提出してしまっています。
これでは査定に響きます。

ではコピー機の完了を待つにはどうすれば良いでしょうか?

そう、ここでPromiseの出番です


ではさっきのコードにPromiseを追加します。

main.js
let paper = 1;

const copy = () => {
  return new Promise((resolve) => {//・・・・・・・・・・・・①
    const id = setInterval(() => {
      console.log(`${paper} 枚目`);
      if (paper >= 10) {
        clearInterval(id);
        resolve(paper);//・・・・・・・・・・・・・・・・・・・・・・・・・・②
      }
      paper++;
    }, 100);
  });
};

console.log('上司:これ10枚コピーしといて');
copy().then((paper) => {//・・・・・・・・・・・・・・・・・・・・・・・・・③
  console.log(`コピー${paper}枚できました!`);
});

変更点が3つあります。


main.js
const copy = () => {
  return new Promise((resolve) => {//・・・・・・・・・・・・①
    //非同期処理
  });
};

非同期処理をnew Promiseのコールバック関数で包んでいます。

これをラップすると言ったりします。

return new Promiseとすることでcopy関数にPromiseを返しています。

引数にresolveを渡しています。

他にrejectという引数も渡せます。


main.js
      if (paper >= 10) {
        clearInterval(id);
        resolve(paper);//・・・・・・・・・・・・・・・・・・・・・・・・・・②
      }

ここで引数のresolveが呼ばれています。
Promiseのコールバック関数ではresolveもしくはrejectが呼ばれるまで次の処理に移りません
ここではpaperが10になった時点でresolveが呼ばれています。
つまりpaperが10になるのを待っているということです。


main.js
copy().then((paper) => {//・・・・・・・・・・・・・・・・・・・・・・・・・③
  console.log(`コピー${paper}枚できました!`);
});

copy関数の後ろに.thenと続いています。
thenメソッドPromiseのメソッドですが、
①でnew Promise を返しているので使用できます。
thenの引数のpaperは②でresolvに渡したものです。
この中に10枚の用紙が入っています。

結果

demo

無事に10枚の用紙を提出できました!


以上です。
間違えてたら遠慮なくご指摘ください!

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

トリボナッチ数列のn番目を求める関数をメモ化してみた!(JavaScript)

はじめに

最近、Udemyでアルゴリズムの講座とかをやってたりするのですが、JavaScriptでフィボナッチ数列のプログラムを書いて、それをメモ化するのが面白かったので、トリボナッチ数列でも少し変えてやってみました。

Stephen Grider先生の講座はどれも分かりやすいです。
参考:The Coding Interview Bootcamp: Algorithms + Data Structures

トリボナッチ数列とは

トリボナッチ数列は 0、0、1 で始まり、以後の項がその前の3つの項の和となる数列です。

引用:フィボナッチ数列(9): トリボナッチ数列とテトラナッチ数列の計算

確認用のテスト

少し冗長かもですが、こんな感じに書いてみました↓

src/test.js
const tri = require("./index");

test("関数が定義されているか", () => {
  expect(typeof tri).toEqual("function");
});

test("n=1の時", () => {
  expect(tri(1)).toEqual(0);
});

test("n=2の時", () => {
  expect(tri(2)).toEqual(1);
});

test("n=3の時", () => {
  expect(tri(3)).toEqual(1);
});

test("n=4の時", () => {
  expect(tri(4)).toEqual(2);
});

test("n=5の時", () => {
  expect(tri(5)).toEqual(4);
});

test("n=50の時", () => {
  expect(tri(50)).toEqual(3122171529233);
});

for文を使ったやり方

一番最初に思い付いたのはfor文を使った基本的なやり方でした。

src/index.js
function tri(n) {
  const arr = [0, 0, 1];
  for (let i = 3; i <= n; i++) {
    arr[i] = arr[i - 3] + arr[i - 2] + arr[i - 1];
  }
  return arr[n];
}

module.exports = tri;

次のコマンドでテストを実行!

jest src/test.js

image.png
うまくいきました!

再帰関数を使ったやり方

再起的なやり方を使って、コードを短くできました!

function tri(n) {
  if (n < 3) return n === 2 ? 1 : 0;
  return tri(n - 3) + tri(n - 2) + tri(n - 1);
}

module.exports = tri;

しかし、このやり方だと、どんどん枝分かれしていって、nが50の時には何回も同じ呼び出しをテストがなかなか終わりません。
なので、以下のようにメモ化をしてみます。

function memorize(fn) {
  const cache = [];
  return function (args) {
    if (cache[args]) return cache[args];
    const result = fn.call(this, args);
    cache[args] = result;
    return result;
  };
}

function slowTri(n) {
  if (n < 3) return n === 2 ? 1 : 0;
  return tri(n - 3) + tri(n - 2) + tri(n - 1);
}

const tri = memorize(slowTri);

module.exports = tri;

少し複雑で分かりにくいかもしれません。一応説明↓

  • triの宣言のところから見ていくと、memorizeはfunction(args){...}を返すので、argsにはnが入る。
  • memorizeの中の関数では、配列cacheにnの時の値があればそれを返して、なければfnの引数nにargsに入っていたnが入って呼び出せれます。その後に求められた値がcacheに格納されます。
  • fnはmemorizeの引数なので、slowTriです。
  • slowTriのなかでnが3以上なら、枝分かれしてtriが呼び出されます。枝分かれしていきnが3より小さくなるまで繰り返されます。

image.png

テストがうまくいきました!

終わりに

ここまで読んでいただきありがとうございました!私の説明が下手で少し分かりにくかったらごめんなさい!
結果的にfor文のやり方より長くなってしまいましたが、素のJavaScriptを使って、関数のメモカを書いたの初めてなので勉強になりました。アルゴリズムの問題は人によって時方が結構違って、毎回何かしら学べるので楽しいです。
感想やご要望などお待ちしております!

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

【Rails】Ajaxでいいねの数を表示する方法

概要

前回の続きでいいねカウントの実装をしていいねの数を表示していきたいと思います。

APIの作成

まずJavaScriptからリクエストが来たときに返したいデータを定義していきます。
JavaScriptでリクエストを送ったら現在のいいね数をresponseで返すことができれば、実装できると思いました。

いいねの数を取得するために、LikesControllerのshowアクション内でlike_countを定義しました。

app/controllers/likes_controller.rb
def show
  like_count = @team.likes.count
  render json: { likeCount: like_count }
end

ただ、私が実装したいことは

現在のいいね数をDOMContentLoadedイベント時に取得することだけでなく、POSTとDELETEのリクエストの時にも取得したいと考えています。
そのため、showアクション内にだけ定義するのではなくcreate, destroyにも定義していく必要があります。createdestroyでもrender jsonlikeCountを入れないとJavaScriptからのリクエストに対応するデータがないのでundefinedになって返ってきてしまいます。

前回と重複するところもありますが、LikesControllerの記述はこのようになると思いました。before_action :like_countsでアクションの最初に読み込むようにしています。

LikesController

app/controllers/likes_controller.rb
class LikesController < ApplicationController
  before_action :authenticate_user!
  before_action :set_like
  before_action :like_counts

  def show
    like_status = current_user.has_liked?(@team)

    render json: { hasLiked: like_status, likeCount: like_counts }.to_json
  end

  def create
    @team.likes.create!(user_id: current_user.id)

    render json: { status: 'ok', likeCount: like_counts }.to_json
  end

  def destroy
    like = @team.likes.find_by!(user_id: current_user.id)
    like.destroy!

    render json: { status: 'ok', likeCount: like_counts }.to_json
  end

  private

  def set_like
    @team = Team.find(params[:team_id])
  end

  def like_counts
    @like_count = @team.likes.count
  end
end

どこにいいねの数を表示するのか

show.html.haml
.star_counter
  現在
  %span
  がいいねをしています。

今回、star_counterクラス内のspanタグ内にいいねの数を入れていこうかと思います。spanを入れた理由はいいねの数をscssでスタイルを調整しようと考えているからです。たとえば、以下のようにすればいいねの数だけ文字が赤く太くなります。

SCSS

scss
.star_counter {
  span {
    color: red; // 好きな色
    font-weight: bold; // 線を太くして強調
  }
}

いいねの数を取得(GET)

JavaScriptで実装したいこととしてはまず、GETリクエストで現在のいいねの数を取得することです。
そのため、GETリクエストが成功したらいいねの数を表示させていきます。

コードにするとこのようになると思いました。 ※前回と重複するところは省略しております。

jQuery
// GET, POST, DELETEリクエストで使うので処理をまとめています。
const likeCountCalculation = (likeCount) => {
// いいねの数を表示させたいところを取得。今回はstar_counterクラス内のspan要素を取得しています。その後appendで追加していくという処理です。
  $('.star_counter > span').append(
    `${likeCount}`
  )
}

// axiosでGETリクエスト成功
.then((response) => {
// response内に先程Controllerで書いたlikeCountがjson形式で返されるので取得。
  const likeCount = response.data.likeCount
// 上で定義してあるlikeCountCalculation()が呼び出されて、span要素内に現在のいいねの数が表示されるようになります。現在のいいねの数はlikeCountに入っているので関数を呼び出すときにを引数にいれています。
  likeCountCalculation(likeCount)
})

いいねの数の増減 (POST, DELETE)

はじめは、likeのcountを+1, -1することで実装すればと思っていたのですが、それよりもGETの処理と同じ考え方で現在のいいねの数を取得すれば良いのでは?と考えました。現在のというのは+1や-1されたあとのいいねの数のことです。要するに、POSTやDELETEリクエストが成功すればいいねの数は増減しているのでその値を取得することで実装できると思いました。
コードにするとこのようになると思います。POSTもDELETEも同じ記述なのでまとめて書かせていただきます。GETの処理と違う点は、リクエストが成功したらspan要素の中身を空にするということです。これをしないとリクエストの度に要素が追加されていってしまいます。

jQuery
// axiosでリクエスト(POST or DELETE)成功
.then((response) => {
  // responseに入ってくるいいねの数を取得
  const likeCount = response.data.likeCount
  // star_counterクラス内のspan要素の中身を空にする。
  $('.star_counter > span').html('')
  // 現在いいねの数を表示する。
  likeCountCalculation(likeCount)
})

まとめ

  • 前回同様やりかたは一つではないこと。
  • 親要素内の子要素を取得する方法もひとつではなく選択肢がたくさんあり、わかりやすいものを使う。(読みやすいコードだったり状況に応じて判断)
  • jsonで複数の値を返すには記述を変える必要がある。

最後に

自分の実装したいことをどうやって実現していくのか、ここの言語化が大事だと感じました。実装したいことに対して、いくつもの方法があるということは、簡単に書けるものだったり逆に複雑になってしまったりすることがあると思います。こういったところもたくさんコードを書いたり技術書を読んだり、記事を見たりして学習していくことで力をつけていきます。

参考文献

前回書いた記事です。いいねの実装をこちらでしているのでよろしければご覧ください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python学習 基礎編 ~数値と計算~

こちらではPython学習の備忘録と、Ruby、JavaScriptとの比較も含め記載していきたいと思います。
プログラミング初心者や他の言語にも興味、関心をお持ちの方の参考になれば幸いです。

数値とは?

プログラミングでは、数値を扱うこともでき、文字列とは違いクォーテーションで囲む必要がない。
クォーテーションをつけると、文字列と解釈されるため、文字列と数値は明確に違うものであることを意識しよう。

計算について

数値は、足し算「+」、引き算「-」、掛け算「*」(アスタリスク)、割り算「/」(スラッシュ)、余剰(割り算の余り)「%」の計算が可能です。
また数値や記号はすべて半角で記述する。

※記号の前後の半角スペースはなくても構わないですが、入れた方がコードが見やすいと思います。

以上は、Python、Ruby、JavaScriptで共通となります。
各言語の記述を下記に記載します。

Python

script.py
print(3)  # 数値のみ
print(3 + 5)  # 対し算
print(6 - 5)  # 引き算
print(3 * 5)  # 掛け算
print(20 / 5)  # 割り算
print(8 % 5)  # 余剰(割り算の余り) 

# コンソール
3
8
1
15
4
3  # 8割る5は、1余り3

Ruby

index.rb
puts 3
puts 3 + 5
puts 6 - 5
puts 3 * 5
puts 20 / 5
puts 8 % 5

JavaScript

script.js
console.log(3);
console.log(3 + 5);
console.log(6 - 5);
console.log(3 * 5);
console.log(20 / 5);
console.log(8 % 5);

おわりに

数値に関しては、各言語ともに共通する部分が多いので覚えやすいですね。次回は文字列の連結などをやろうかな...では!

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

jQuery学習#02

4. フォーム送信前にエラーチェックする

HTML:

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>jQuery テスト</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <form class="my-form" action="" method="post">
    同じテキストを入力してください。
    <p>
      <input type="text" name="text1" class="text1">
    </p>
    <p>
      <input type="text" name="text2" class="text2">
    </p>
    <p>
      <input type="submit" value="送信">
    </p>
    <p class="error"></p>
  </form>
  <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
  <script src="main.js"></script>
</body>
</html>

CSS:

.error {
  color: #f00;
}

↓できあがり。※この状態ではまだ何も起こらない。

JavaScriptを追加する。

$('.my-form').on('submit', (e) => {
  const text1 = $('.text1').val();
  const text2 = $('.text2').val();

  if (text1 !== text2) {
    e.preventDefault();

    $('.error').html('テキストが一致しません');
  }
});

説明。の前に、今回必要なメソッド等の記述。
・submitイベント
フォーム送信をしたとき、ページ遷移する前に発生する。

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

・html()メソッド
HTML要素を取得したり追加・書き換えを行える。

説明。
<form class="my-form" action="" method="post"></form>内のフォームに入力、送信をしたとき、ページが変わる前に下記関数を実行する。

(e) => {
  const text1 = $('.text1').val();
  const text2 = $('.text2').val();

  if (text1 !== text2) {
    e.preventDefault();

    $('.error').html('テキストが一致しません');
  }
}

↑関数。
定数text1<input type="text" name="text1" class="text1">のvalue属性を取得したものを代入。
定数text2<input type="text" name="text2" class="text2">のvalue属性を取得したものを代入。
もし、定数text1text2が異なるなら、イベントをキャンセルし、<p class="error">のhtml要素にテキストが一致しませんを追加する。

これで、上下のフォームに異なるものを入力すると、下記のように表示される。

5. 後から追加した要素にイベントハンドラを追加する

まず、イベントハンドラだが、jQueryでは、「イベントが発生したときに呼び出される関数」をイベントハンドラと呼ぶのが一般的とのこと。

HTML:

 <div id="parent">
    <p class="child">もともとの要素</p>
  </div>

JavaScript:

$('.child').on('click', () => {
  console.log('.childのイベントハンドラ');
});

$('#parent').append('<p class="child">追加した要素</p>');

<p class="child">もともとの要素</p>がクリックされたとき、コンソールに.childのイベントハンドラを出力する。

<div id="parent">の末尾に<p class="child">追加した要素</p>を追加する。

・appendメソッド
対象の要素の末尾に、テキストやHTML要素を追加するメソッド。

これでスクリーンには下記のように表示される。

が、このとき追加された要素をクリックしてもコンソールには.childのイベントハンドラとは出力されない。
なぜか? 追加された要素は、イベントハンドラを登録した後に追加した要素だからだ。
※イベントハンドラは基本的に、すでに存在する要素にだけ登録できる。

では、どうすれば追加された要素をクリックしたときに、コンソールに.childのイベントハンドラを出力させられるか?

まず、↓のようにJavaScriptを変更する。

$('#parent').on('click', '.child', () => {
  console.log('.childのイベントハンドラ');
});

$('#parent').append('<p class="child">追加した要素</p>');

これで、追加された要素をクリックしたときに、コンソールに.childのイベントハンドラを出力することができる。
↑コードではイベントのバブリングフェーズを利用している。
バブリングフェーズの詳しい説明は割愛。(いずれ記述する)。

6. イベントハンドラにデータを渡す

HTML: <div class="my-click child">クリックしてください</div>

JavaScript:

$('.my-click').on('click', { test: 'テストデータ' }, (e) => {
  console.log('追加したデータ:', e.data.test);
});

任意のデータを追加したい場合は、onメソッドの第2引数に、追加したいデータをオブジェクトとして渡す。
このデータをイベントオブジェクトのdataプロパティから取得できる。
・dataプロパティ
イベント時にイベントハンドラに引き渡されたイベントデータが格納されているプロパティ。

#7. 要素の表示/非表示

要素を非表示にしたいときは、hideメソッドを使う。

<div class="box">テキスト</div>
$('.box').hide();

非表示にした要素を表示するには、showメソッドを使う。

$('.box').show();

表示、非表示を切り替えたいだけのときは、toggleメソッドを使う。

$('.box').toggle();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Serverlessを使ってブラウザだけでJavaScriptを実行してみる

はじめに

Serverlessのクラウドサービスを利用して、JavaScriptを実行(ランタイムはNode.js)してみました。常時稼働しなくて良いが、リクエスト投げた時だけ動いて欲しい。定期的に実行したい。そんな時に使えそうです。

ここではIBM Cloud Functions(以下、ICF)を使いました。
当記事は「サーバーレスというものを試してみたい」「IBM Cloud Functionsを使ったことはない」という方向けの記事なので、より深い内容は別記事をあたっていただければと思います。
なお、厳密にはライト(無償)プランではなく、平均実行時間や実行回数などで課金されていくのですが、記事を書いている時点(2020/11)ではライトアカウント(無償)でも使えました。また無料枠があるので、ここに記載されている内容を試すくらいであれば無料で試せると思います。

こちらのチュートリアルを基に少し手を加えたものを記事にしています。

他に参考にした記事
- マニュアル: ここにチュートリアルがあります。
- セミナー資料: ICFの紹介とハンズオン手順があります。
- なお、IBM Cloudを利用されたことがない方が始めるための手順の記事はこちら

では、早速試してみます。順調にいけば1時間足らずで全て試せると思います。


IBM Cloud Functionsのアクションを作成する

アクションとは、「サーバーレスで動かすコード」といったところでしょうか。単体の実行や、単体の実行結果を渡して次につなげる「シーケンス」というものもできます。

  1. IBM Cloudにログイン
    https://cloud.ibm.com/

  2. 左上のナビゲーション・メニューをクリックし、「Functions」を選択

  3. (この手順はやらなくても先に進めますが)名前空間(namespace)を作ります。
    namespaceとは、ICFで作成・実行するアクションやトリガーなどの入れ物です。ユーザーごとにアクセス権限管理もできるようです。

画面上部の「現在の名前空間」のプルダウンをクリックし、一番下の「名前空間の作成」をクリックします。

  • 名前: 任意。ここではfunctions-test
  • 以下はそのままで「作成」をクリック

現在の名前空間: functions-test がでてくれば成功です。
以降の手順はこの名前空間で作業していきます。
ちなみにこの名前空間、一旦作ると消すことができないみたいです・・・。

4.「作成の開始」をクリック
5. 「作成」画面で一番左の「アクション」をクリック
6. 「アクションの作成」画面で

  • アクション名: 任意。ここでは hello
  • 以下はそのまま で「作成」をクリック

7.コードのエディターが開きます。Hello Worldのサンプルコードが入っています。右上の「起動」をクリックして試してみます。
8. 以下のように実行できました。
icf_helloworld.png

パラメータを使ってアクションを呼び出す

9.少しアプリを変えてみましょう。エディターで、コードから、function main(params) {...} 部分を削除するか、同じ部分をコメントアウト /* ... */で囲みます。その後、以下をコピペし、右上の「保存」をクリック。
このコードは、引数にnameとplaceがあり、これを渡すことで文を出力します。

 function main(params) {
    return { message: "Hello, " + params.name + " from " + params.place };
 }

10.「パラメータを付けて起動」をクリックするとポップアップが現れるので、以下のように{}で値を囲むようにコピペ(nameとplaceの値(sirotan, Japan)は適当なので、適宜変更してください)し、「適用」。

{
   "name": "sirotan",
   "place": "Japan"
}

11.その後「起動」すると、以下のように結果が変わりました。
icf_helloworld2.png

トリガーでアクションを呼び出す

トリガーとは、先ほど作成したアクションを定期的に実行したり、何らかのイベントをきっかけに実行するためのものです。

12.以下のように左上の「Actions」をクリックして元のメニューに戻ります。
※ このWebサイトのツリー構造を示すものをパンくずリストっていうらしい。筆者は初めて知りました(汗
pankuzu.png

13.もう一つアクションを作成します。手順5~6と同様です。アクション名は先ほどのもの(hello)とは違うものにしてください。
- アクション名: 任意。ここでは gettime
- 以下はそのまま で作成をクリック

14.以下のコードをコピペします。エディターで、コードの中身の function main {...} 部分を削除するか、コメントアウト /* ... */で囲みます。その後、以下をコピペし、「保存」します。
このコードでは、実行時の日時と、何回目の実行かを表示します。

 var counter = 0; // global variable
 function main(msg) {
     var date = new Date(Date.now() + ((new Date().getTimezoneOffset() + (9 * 60)) * 60 * 1000));
     var time = date.getHours() + ":" + date.getMinutes() + ":" +
     date.getSeconds();
     counter++;
     return { message: "It is " + time + ". This action has been called " + counter + " time(s)." };
 }

15.このgettimeアクションを繰り返し実行させたいと思います。コードエディターの画面左「接続されたトリガー」をクリック。その後、右上の「トリガーの追加」をクリック
16. 「Periodic」をクリック
17. 以下を設定し、「作成&接続」をクリック

  • トリガー名: 任意。ここでは minute alarm
  • タイマー設定: 一番下の「UTC分」の右上のパターン選択から「毎分」を選択

18.左上のパンくずリストから「Action」->左メニューの「モニター」を選択。トリガーに接続されたアクション(minute alarm)が、赤枠のように約1分おきに呼び出され、アクション「hello」を実行されていることが確認できるかと思います。
また、画面右の「アクティベーション・ログ」に実行ログが出ていますが、青枠のアクション名(helloやminute alarm)の下にある文字列は実行時のID(Activation ID)です。クリックするとブラウザの別ウィンドウが開き、実行内容が表示されます。

icf_monitor.png

19.作成したトリガーを停止します。
左メニューの「トリガー」-> 該当のトリガー(ここでは、minute alarm)をクリック ->右の「接続」列にある「有効」のチェックを外す。
これを忘れるとずっと実行され続け、意図せずに料金が発生する恐れがありますのでご注意ください。

Webアクションを作成する

これまではICFの画面内での実行結果表示でしたが、HTMLで実行結果を表示したいと思います。

20.先ほど作成したアクション: helloのコードを以下のようにupdateし、右上の「保存」をクリック。
これをHTML形式でメッセージを表示します。

function main(params) {
    let html = '<html style="color:red"><body><p>' +
    'Hello, ' + params.name + ` from ` + params.place +
    '</p></body></html>'
    return { headers: { "Content-Type": "text/html" },
             body: html };
}

21.左メニューの「エンドポイント」をクリックし、「Webアクションとして有効化」にチェックして、「保存」。
22. 画面中程の「HTTP メソッド」にあるURLリンクを開くとメッセージが表示されます。
23. 引数を渡すには、ブラウザのURLの最後(/helloの後)に以下のように値を追加します

?name=sirotan&place=Japan

HTMLで表示されました。
icf_helloworld_web.png

以上です。
同様に、gettimeアクションでも可能です。コードを以下のようにしてみてください。

var counter = 0; // global variable 
function main(msg) {
     var date = new Date(Date.now() + ((new Date().getTimezoneOffset() + (9 * 60)) * 60 * 1000));
     var time = date.getHours() + ":" + date.getMinutes() + ":" +
     date.getSeconds();
     counter++;

    let html = '<html style="color:red"><body><p>' +
    'It is ' + time + '. This action has been called ' + counter + ' time(s).'
    '</p></body></html>'
    return { headers: { "Content-Type": "text/html" },
             body: html };
}

まとめ

ここまで読んでいただきありがとうございます。
割と簡単に、ブラウザだけでJavascriptを実行し、コード変更や定期実行、実行結果をHTML表示できました。
ちょっとでも興味を持っていただければ幸いです。

他の言語、CLIでの実行、アクションのAPI呼び出しや、複数のアクションの実行(シーケンス)も可能ですが、それはまた別の機会に試そうと思います。

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

【jQuery】スクロールの位置によってクラスを付与する方法【JavaScript】

参考記事

https://hacknote.jp/archives/47954/

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>jQueryのscrollイベントによるCSSクラスの付け替え</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="app">
    <header class="header">
      header
    </header>

    <nav class="fixed-nav">
      navigation
    </nav>

    <section class="section1">

      <h1>Title</h1>
      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Magni, enim quas? Dolorum nesciunt quam aut, quae suscipit unde blanditiis atque eius quia saepe culpa, cupiditate cum quo eveniet deleniti nulla.
      </p>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Et nulla hic natus nobis eligendi. Quam fugit et perferendis suscipit voluptas enim ipsa eveniet dolores eum? Consectetur odio labore voluptatem reprehenderit.
      </p>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Et nulla hic natus nobis eligendi. Quam fugit et perferendis suscipit voluptas enim ipsa eveniet dolores eum? Consectetur odio labore voluptatem reprehenderit.
      </p>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Et nulla hic natus nobis eligendi. Quam fugit et perferendis suscipit voluptas enim ipsa eveniet dolores eum? Consectetur odio labore voluptatem reprehenderit.
      </p>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Et nulla hic natus nobis eligendi. Quam fugit et perferendis suscipit voluptas enim ipsa eveniet dolores eum? Consectetur odio labore voluptatem reprehenderit.
      </p>

    </section>

    <section class="section2">
      <h1>
        title
      </h1>
      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Similique nobis quod vel odit aliquam nisi in, commodi possimus ex. Quo error numquam libero odit, at enim soluta asperiores suscipit quae.
      </p>
      <p>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, nisi nihil! Sapiente, nulla. Officiis officia animi, dicta, odio culpa et error, iure sit cum dolor veritatis perferendis minus recusandae facere.
      </p>
      <p>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, nisi nihil! Sapiente, nulla. Officiis officia animi, dicta, odio culpa et error, iure sit cum dolor veritatis perferendis minus recusandae facere.
      </p>
      <p>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, nisi nihil! Sapiente, nulla. Officiis officia animi, dicta, odio culpa et error, iure sit cum dolor veritatis perferendis minus recusandae facere.
      </p>
      <p>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, nisi nihil! Sapiente, nulla. Officiis officia animi, dicta, odio culpa et error, iure sit cum dolor veritatis perferendis minus recusandae facere.
      </p>
    </section>

    <footer class="footer">

    </footer>
  </div>


  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  <script src="app.js"></script>
</body>
</html>
app.js
window.onscroll = function () {
  scrollToggleClass(".section1", ".fixed-nav", "show");
}
function scrollToggleClass(rangeTarget, addTarget, classname) {
  if($(rangeTarget).length){
    scroll = $(window).scrollTop();
    startPos = $(rangeTarget).offset().top;
    endPos = startPos + $(rangeTarget).outerHeight();
    if (scroll > startPos && scroll < endPos) {
        $(addTarget).addClass(classname);
    } else {
        $(addTarget).removeClass(classname)
    }
  }
}

// scrolltoggleClass()関数は自作の関数のようだ
// ここでは引数の順番から、$(rangeTarget)は'.section1'、$(addTarget)は、$('.fixed-nav'), classnameは'show'というクラスの名前を示している
// showというクラスが追加されたら、CSSで黒いヘッダーが表示されるようにする


style.css
body {
  position: relative;
  background: #666;
  margin: 0;
}
header, footer {
  background: #fff;
  padding: 30px;
}
.fixed-nav {
  position: fixed;
  top: 0;
  width: 100vw;
  background: #000;
  color: #fff;
  padding: 20px 30px;
  opacity: 0;
  transform: translateY(-100%);
  transition: all .3s ease; 
}
.fixed-nav.show {
  opacity: 1;
  transform: none;
}
section {
  max-width: 600px;
  margin: 60px auto;
  padding: 20px 40px;
  background: #fff;
  border-radius: 8px;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript 地図ライブラリ Leaflet

初めに

JavaScript地図ライブラリのLeafletの基本処理サンプル
良く忘れるのでメモ

フォルダ構成

├─ index.html
└─ assets
    ├─ icon
    │   ├─ current.png
    │   └─ point.png
    └─ js
        └─ leaflet_common.js

※画像も用意する必要あり

JavaScript(処理共通)

leaflet_common.js
// *******************************************
// Leaflet共通処理
// 地図表示、アイコン表示、表示アイコン初期化
// ※注意書き
//  オープンストリートマップを使っている(国土地理院を使う場合はリンク先を変更する必要あり)
//   varになっているmapObj、putIconsObjについては、画面上で操作する必要あり
// *******************************************

// マップオブジェクト
var mapObj = null;
// 地図のリンク先
const MAP_LINK = '<a href="http://openstreetmap.org">OpenStreetMap</a>';
// 地図のタイルURL
const MAP_TILE = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'

// 初期のズーム値(小さいほうが引き)
const START_ZOOM = 15;
// 最大のズーム値
const MAX_ZOOM = 20;
// 最大のズーム値
const ICON_FOLDER = "./assets/icon/";
// アイコンのファイルパス定義
const ICON_IMAGE = {
    0: "current.png",
    1: "point.png",
}
// アイコンのサイズ
const ICON_SIZE = 36;
// アイコンオブジェクト
var putIconsObj = [];
// 現在地アイコンオブジェクト
var currentIconObj = [];


// --------------------------------------------
// 地図表示関数
// cur_lat_t : 表示する中心緯度
// cur_lng_t : 表示する中心経度
// --------------------------------------------
const viewMap = function(cur_lat_t, cur_lng_t){

    // マップオブジェクトに設定現在地と初期ズーム値を設定
    mapObj = L.map('map_area').setView([cur_lat_t, cur_lng_t], START_ZOOM);
    // 地図表示情報を設定
    L.tileLayer(
        MAP_TILE,
        {
            attribution: 'Map data &copy; ' + MAP_LINK, // リンク元
            maxZoom: MAX_ZOOM                           // 最大ズーム値
        }
    ).addTo(mapObj);
};

// --------------------------------------------
// 1つのアイコンを表示する
//  lat_t:表示緯度
//  lng_t:表示経度
//  imgIdx:アイコン画像のインデックス(ICON_IMAGE参照)
//  popup_value:ポップアップ表示内容
//  iconSize:アイコンのサイズ / 形式は [横, 縦] で指定
// --------------------------------------------
const putIcon = function(lat_t=0.0, lng_t=0.0, imgIdx=0, popup_value=null, iconSize=[ICON_SIZE, ICON_SIZE]){

    // アイコン情報を設定取得
    var pointIcon = L.icon({
        iconUrl: ICON_FOLDER + ICON_IMAGE[imgIdx], // 画像のURL
        iconSize: iconSize,                        // アイコンサイズ
    });

    // 取得するアイコンオブジェクト
    let iconObj = null;
    // アイコン設定
    iconObj = L.marker([lat_t, lng_t], {icon: pointIcon}).addTo(mapObj);
    // ポップアップがある場合
    if(popup_value){
        // ポップアップ内容をアイコンに設定
        iconObj.bindPopup(popup_value);
        // アイコンにポップアップイベントを設定
        iconObj.on("click", function(){
            this.openPopup();
        });
    }
    return iconObj;
};

// --------------------------------------------
// 地図上のアイコンを初期化(アイコンを再表示する際などに使用)
// --------------------------------------------
const dropIcons = function(){
    if(putIconsObj.length){
        // 地図上のアイコンを初期化
        for(idx in putIconsObj){
            // 地図上から表示を外す
            mapObj.removeLayer(putIconsObj[idx]);
            // ポップアップイベントをオフにする
            putIconsObj[idx].off("click");
        }
    }
    // アイコンオブジェクト配列を初期化
    putIconsObj = [];
}

HTML(使い方)

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta http-equiv="content-language" content="ja">
<title>Leaflet サンプル</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<style>
    /* ページ全体 */
    html, body  {
        width: 100vw;
        height: 100vh;
        padding: 0px;
        margin: 0px;
    }
    /* 地図領域(サイズはお好みで) */
    #map_area {
        width: 100%;
        height: 100%;
    }
</style>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script src="assets/js/leaflet_common.js"></script>
<script>
    // 場所情報(仮作成@博多近辺)
    var tempPlaceData = [
        {"id":"1", "lat": 33.59513931435894,  "lng": 130.42419433593753, "name":"地点A"},
        {"id":"2", "lat": 33.59260123175435,  "lng": 130.41131973266604, "name":"地点B"},
        {"id":"3", "lat": 33.59517506146791,  "lng": 130.42694091796878, "name":"地点C"},
        {"id":"4", "lat": 33.59653344063089,  "lng": 130.420138835907,   "name":"地点D"},
        {"id":"5", "lat": 33.590813804823924, "lng": 130.42249917984012, "name":"地点E"},
        {"id":"6", "lat": 33.590849553725455, "lng": 130.4186797142029,  "name":"地点F"},
    ];

    // 画面初期表示時
    window.onload = function(){
        // 緯度経度取得関数(引数は下記の事後処理)
        navigator.geolocation.getCurrentPosition(gcpAfterFunc);
    };

    // 座標取得後の初期処理
    const gcpAfterFunc = function(position){

        // データ取得関数(可変の場合は、ajax処理等で取ってくる)
        let placeData = tempPlaceData;

        //緯度経度取得
        let lat_t = position.coords.latitude;
        let lng_t = position.coords.longitude;

        //地図表示関数
        viewMap(lat_t, lng_t);

        // 地図のイベント処理は、このタイミングで指定
        mapObj.on('click', testfunc);

        // アイコン表示
        putIconsData(placeData, 1);

        // 現在地のアイコンを表示し、オブジェクトを主特区
        currentIconObj = putIcon(lat_t, lng_t, 0);

    }

    // 渡されたjsonデータのアイコンを表示する(サンプルでは、戻り値での判定は行っていない)
    const putIconsData = function(jsonData, imgIdx = 0){
        // 処理されたか
        let isProcessed = false;

        // インデックスに対する画像がある場合は実行
        if(ICON_IMAGE[imgIdx]){

            for(var idx in jsonData){
                // アイコンを地図上に設定、ポップアップで地点名を設定し、オブジェクトとして取得、
                putIconsObj[idx] = putIcon(jsonData[idx]["lat"], jsonData[idx]["lng"], imgIdx, jsonData[idx]["name"]);
                // ※ポップアップ表示したい場合やアイコンサイズを変えたい場合は以下のように指定
                // putIconsObj[idx] = putIcon(jsonData[idx].lat, jsonData[idx].lng, imgIdx, jsonData[idx]["name"], [36, 48]);

                // 実行した場合はtrueとする
                if(!isProcessed) isProcessed = true;
            }
        }

        // ループで処理がされている場合は、trueを返す
        return isProcessed;
    }

    // テスト関数
    const testfunc = function(e){

        let lat_t = e.latlng.lat;
        let lng_t = e.latlng.lng;

        console.log([lat_t, lng_t]);
    }
</script>
</head>
<body>
    <div id="map_area"></div>
</body>
</html>

※専用のcssとjsは読み込む必要あり
※上記のjsも読み込む必要あり

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

JavaScript 地図ライブラリ Leaflet 基本処理サンプル

初めに

JavaScript地図ライブラリのLeafletの基本処理サンプル
良く忘れるのでメモ

フォルダ構成

├─ index.html
└─ assets
    ├─ icon
    │   ├─ current.png
    │   └─ point.png
    └─ js
        └─ leaflet_common.js

※画像も用意する必要あり

JavaScript(処理共通)

leaflet_common.js
// *******************************************
// Leaflet共通処理
// 地図表示、アイコン表示、表示アイコン初期化
// ※注意書き
//  オープンストリートマップを使っている(国土地理院を使う場合はリンク先を変更する必要あり)
//   varになっているmapObj、putIconsObjについては、画面上で操作する必要あり
// *******************************************

// マップオブジェクト
var mapObj = null;
// 地図のリンク先
const MAP_LINK = '<a href="http://openstreetmap.org">OpenStreetMap</a>';
// 地図のタイルURL
const MAP_TILE = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'

// 初期のズーム値(小さいほうが引き)
const START_ZOOM = 15;
// 最大のズーム値
const MAX_ZOOM = 20;
// 最大のズーム値
const ICON_FOLDER = "./assets/icon/";
// アイコンのファイルパス定義
const ICON_IMAGE = {
    0: "current.png",
    1: "point.png",
}
// アイコンのサイズ
const ICON_SIZE = 36;
// アイコンオブジェクト
var putIconsObj = [];
// 現在地アイコンオブジェクト
var currentIconObj = [];


// --------------------------------------------
// 地図表示関数
// cur_lat_t : 表示する中心緯度
// cur_lng_t : 表示する中心経度
// --------------------------------------------
const viewMap = function(cur_lat_t, cur_lng_t){

    // マップオブジェクトに設定現在地と初期ズーム値を設定
    mapObj = L.map('map_area').setView([cur_lat_t, cur_lng_t], START_ZOOM);
    // 地図表示情報を設定
    L.tileLayer(
        MAP_TILE,
        {
            attribution: 'Map data &copy; ' + MAP_LINK, // リンク元
            maxZoom: MAX_ZOOM                           // 最大ズーム値
        }
    ).addTo(mapObj);
};

// --------------------------------------------
// 1つのアイコンを表示する
//  lat_t:表示緯度
//  lng_t:表示経度
//  imgIdx:アイコン画像のインデックス(ICON_IMAGE参照)
//  popup_value:ポップアップ表示内容
//  iconSize:アイコンのサイズ / 形式は [横, 縦] で指定
// --------------------------------------------
const putIcon = function(lat_t=0.0, lng_t=0.0, imgIdx=0, popup_value=null, iconSize=[ICON_SIZE, ICON_SIZE]){

    // アイコン情報を設定取得
    var pointIcon = L.icon({
        iconUrl: ICON_FOLDER + ICON_IMAGE[imgIdx], // 画像のURL
        iconSize: iconSize,                        // アイコンサイズ
    });

    // 取得するアイコンオブジェクト
    let iconObj = null;
    // アイコン設定
    iconObj = L.marker([lat_t, lng_t], {icon: pointIcon}).addTo(mapObj);
    // ポップアップがある場合
    if(popup_value){
        // ポップアップ内容をアイコンに設定
        iconObj.bindPopup(popup_value);
        // アイコンにポップアップイベントを設定
        iconObj.on("click", function(){
            this.openPopup();
        });
    }
    return iconObj;
};

// --------------------------------------------
// 地図上のアイコンを初期化(アイコンを再表示する際などに使用)
// --------------------------------------------
const dropIcons = function(){
    if(putIconsObj.length){
        // 地図上のアイコンを初期化
        for(idx in putIconsObj){
            // 地図上から表示を外す
            mapObj.removeLayer(putIconsObj[idx]);
            // ポップアップイベントをオフにする
            putIconsObj[idx].off("click");
        }
    }
    // アイコンオブジェクト配列を初期化
    putIconsObj = [];
}

HTML(使い方)

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta http-equiv="content-language" content="ja">
<title>Leaflet サンプル</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<style>
    /* ページ全体 */
    html, body  {
        width: 100vw;
        height: 100vh;
        padding: 0px;
        margin: 0px;
    }
    /* 地図領域(サイズはお好みで) */
    #map_area {
        width: 100%;
        height: 100%;
    }
</style>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script src="assets/js/leaflet_common.js"></script>
<script>
    // 場所情報(仮作成@博多近辺)
    var tempPlaceData = [
        {"id":"1", "lat": 33.59513931435894,  "lng": 130.42419433593753, "name":"地点A"},
        {"id":"2", "lat": 33.59260123175435,  "lng": 130.41131973266604, "name":"地点B"},
        {"id":"3", "lat": 33.59517506146791,  "lng": 130.42694091796878, "name":"地点C"},
        {"id":"4", "lat": 33.59653344063089,  "lng": 130.420138835907,   "name":"地点D"},
        {"id":"5", "lat": 33.590813804823924, "lng": 130.42249917984012, "name":"地点E"},
        {"id":"6", "lat": 33.590849553725455, "lng": 130.4186797142029,  "name":"地点F"},
    ];

    // 画面初期表示時
    window.onload = function(){
        // 緯度経度取得関数(引数は下記の事後処理)
        navigator.geolocation.getCurrentPosition(gcpAfterFunc);
    };

    // 座標取得後の初期処理
    const gcpAfterFunc = function(position){

        // データ取得関数(可変の場合は、ajax処理等で取ってくる)
        let placeData = tempPlaceData;

        //緯度経度取得
        let lat_t = position.coords.latitude;
        let lng_t = position.coords.longitude;

        //地図表示関数
        viewMap(lat_t, lng_t);

        // 地図のイベント処理は、このタイミングで指定
        mapObj.on('click', testfunc);

        // アイコン表示
        putIconsData(placeData, 1);

        // 現在地のアイコンを表示し、オブジェクトを取得
        currentIconObj = putIcon(lat_t, lng_t, 0);

    }

    // 渡されたjsonデータのアイコンを表示する(サンプルでは、戻り値での判定は行っていない)
    const putIconsData = function(jsonData, imgIdx = 0){
        // 処理されたか
        let isProcessed = false;

        // インデックスに対する画像がある場合は実行
        if(ICON_IMAGE[imgIdx]){

            for(var idx in jsonData){
                // アイコンを地図上に設定、ポップアップで地点名を設定し、オブジェクトとして取得、
                putIconsObj[idx] = putIcon(jsonData[idx]["lat"], jsonData[idx]["lng"], imgIdx, jsonData[idx]["name"]);
                // ※ポップアップ表示したい場合やアイコンサイズを変えたい場合は以下のように指定
                // putIconsObj[idx] = putIcon(jsonData[idx].lat, jsonData[idx].lng, imgIdx, jsonData[idx]["name"], [36, 48]);

                // 実行した場合はtrueとする
                if(!isProcessed) isProcessed = true;
            }
        }

        // ループで処理がされている場合は、trueを返す
        return isProcessed;
    }

    // テスト関数
    const testfunc = function(e){

        let lat_t = e.latlng.lat;
        let lng_t = e.latlng.lng;

        console.log([lat_t, lng_t]);
    }
</script>
</head>
<body>
    <div id="map_area"></div>
</body>
</html>

※専用のcssとjsは読み込む必要あり
※上記のjsも読み込む必要あり

画面

image.png
※chromeのスマホデバッグ

あとがき

引数で初期値を設定しているため、IEでは動きません。

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

Vue-multiselectのカスタマイズ方法

概要

Vue-multiselectはセレクトボックスを簡単に実装できる便利なVueライブラリです。
複数選択や自動補完など一般的にセレクトボックスに求められる機能はほぼ全て実現できます。
ここでは公式サイトを元にVue-multiselectのカスタマイズ方法の一部を紹介します。

基本の使い方

スクリーンショット 2020-11-27 4.33.37.png

上図のようにシンプルな単一選択のセレクトボックスは以下のコードで実装できます。
なおCDNを利用するとVue-multiselectを簡単に試せます。

See the Pen RwGwBaO by shi-ma (@shi-ma) on CodePen.

optionsプロパティには選択肢に設定したい配列を指定します。

custom-labelプロパティに関数を指定すると、その返り値が選択肢として表示されるラベルとなります。
optionsが連想配列で各キーのバリューを組み合わせたラベルを作りたい時などは非常に便利です。
一つのキーのバリューのみをラベルとして表示したいときは、代わりにlabelプロパティを使いlabel=countryというような書き方ができます。

custom-labellabelは必須プロパティではありませんが、optionsが単なる配列でなく、連想配列の場合は設定しないと要素がそのまま表示されてしまいます。

スクリーンショット 2020-11-27 4.03.57.png

Events

Vue-multiselectにはイベントプロパティが用意されており、ユーザーの操作をトリガーにして関数を実行することが可能です。

イベントプロパティ一覧

プロパティ名 トリガー
@input this.valueの値が変わる
@select 選択肢を選ぶ
@remove 複数選択の時に選択を取り消す
@search-change 検索クエリが変更される
@tag タグが追加される
@open ドロップダウンが開かれる
@close ドロップダウンが閉じられる

@removeの利用例

See the Pen Untitled2 by shi-ma (@shi-ma) on CodePen.

例えば複数選択をオン(multiple="true")にして@removeプロパティを上記のように設定すると、選択を取り消した時にアラートが表示されます。

ezgif-3-08752ffc97a4.gif

自動補完

Vue-multiselectはデフォルトで自動補完機能がオンになっており、オフにしたい時はsearchableプロパティをfalseにします。
この自動補完はlabelプロパティの値(custom-labelプロパティを指定していればその値)を元に候補を絞っています。

もし独自の検索条件を設定したい場合はカスタマイズできます。

カスタマイズ例

See the Pen VwKwBMN by shi-ma (@shi-ma) on CodePen.

internal-searchプロパティをfalseにするとVue-multiselectの内部検索エンジンがオフになり、optionsを自由に設定できるようになります。

@search-changeに指定した関数は検索クエリに変更がある度に実行され、さらに関数の第一引数には検索クエリが入ります。これを利用し、optionsをフィルタリングするような関数を書けば、検索クエリが変わる度にoptionsが更新されるようになり、自動補完機能を代替できます。

ezgif-1-51043d6a0877.gif

終わりに

Vue-multiselectは用途に合わせてカスタマイズできる余地が大きいライブラリです。
ここでは現時点で日本語での説明が比較的少ない機能を紹介しましたが、Vue-multiselectには面白いカスタマイズ方法がまだまだあります。
Vueでセレクトボックスを実装するときはぜひVue-multiselectをお試しください。

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

セレクトボックスライブラリ「Vue-multiselect」のカスタマイズ方法

概要

Vue-multiselectはセレクトボックスを簡単に実装できる便利なVueライブラリです。
複数選択や自動補完など一般的にセレクトボックスに求められる機能はほぼ全て実現できます。
ここでは公式サイトを元にVue-multiselectのカスタマイズ方法の一部を紹介します。

基本の使い方

スクリーンショット 2020-11-27 4.33.37.png

上図のようにシンプルな単一選択のセレクトボックスは以下のコードで実装できます。
なおCDNを利用するとVue-multiselectを簡単に試せます。

See the Pen RwGwBaO by shi-ma (@shi-ma) on CodePen.

optionsプロパティには選択肢に設定したい配列を指定します。

custom-labelプロパティに関数を指定すると、その返り値が選択肢として表示されるラベルとなります。
optionsが連想配列で各キーのバリューを組み合わせたラベルを作りたい時などは非常に便利です。
一つのキーのバリューのみをラベルとして表示したいときは、代わりにlabelプロパティを使いlabel=countryというような書き方ができます。

custom-labellabelは必須プロパティではありませんが、optionsが単なる配列でなく、連想配列の場合は設定しないと要素がそのまま表示されてしまいます。

スクリーンショット 2020-11-27 4.03.57.png

Events

Vue-multiselectにはイベントプロパティが用意されており、ユーザーの操作をトリガーにして関数を実行することが可能です。

イベントプロパティ一覧

プロパティ名 トリガー
@input this.valueの値が変わる
@select 選択肢を選ぶ
@remove 複数選択の時に選択を取り消す
@search-change 検索クエリが変更される
@tag タグが追加される
@open ドロップダウンが開かれる
@close ドロップダウンが閉じられる

@removeの利用例

See the Pen Untitled2 by shi-ma (@shi-ma) on CodePen.

例えば複数選択をオン(multiple="true")にして@removeプロパティを上記のように設定すると、選択を取り消した時にアラートが表示されます。

ezgif-3-08752ffc97a4.gif

自動補完

Vue-multiselectはデフォルトで自動補完機能がオンになっており、オフにしたい時はsearchableプロパティをfalseにします。
この自動補完はlabelプロパティの値(custom-labelプロパティを指定していればその値)を元に候補を絞っています。

もし独自の検索条件を設定したい場合はカスタマイズできます。

カスタマイズ例

See the Pen VwKwBMN by shi-ma (@shi-ma) on CodePen.

internal-searchプロパティをfalseにするとVue-multiselectの内部検索エンジンがオフになり、optionsを自由に設定できるようになります。

@search-changeに指定した関数は検索クエリに変更がある度に実行され、さらに関数の第一引数には検索クエリが入ります。これを利用し、optionsをフィルタリングするような関数を書けば、検索クエリが変わる度にoptionsが更新されるようになり、自動補完機能を代替できます。

ezgif-1-51043d6a0877.gif

終わりに

Vue-multiselectは用途に合わせてカスタマイズできる余地が大きいライブラリです。
ここでは現時点で日本語での説明が比較的少ない機能を紹介しましたが、Vue-multiselectには面白いカスタマイズ方法がまだまだあります。
Vueでセレクトボックスを実装するときはぜひVue-multiselectをお試しください。

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

THREE Image Transitionを自分サイトに取り入れる

概要

HTMLのスライダーをテンプレートを使ってサクッと実装しようと思い、PHOTPSHOPVIPさんのサイトの中にあった、THREE Image Transitionを自分のサイトに取り入れてスライダーを実装しようと思いました。
しかし、少しハマってしまいました。
そのまま、コピー&ペーストではダメな様です。
そこで、自分なりの解決方法をメモも兼ねて記載しておきます。
(初投稿のため、至らない部分がありましたら申し訳ございません。)

解決方法

テンプレートに加えて、以下のプログラムをindex.html上に追加しました。

index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/bas.js"></script>
<link href="css/styles_myself.css" rel="stylesheet">
<script src="js/scripts_myself.js"></script>

styles_myself.cssとscripts_myself.jsは、サイト上のテンプレートをそのままコピペしてあります。
ファイルのパスは各々調節してください。

その後の調整

実装が完了すると以下の様になると思います。
スクリーンショット 2020-11-27 17.21.06.png
しかし、このままだとスライダーだけのページになってしまうので、追加したcssを編集します。

styles_myself.css
body {
  margin: 0;
  /* overflow: hidden; */
}

canvas {
  /* background-image: radial-gradient(#666, #333); */
}

#instructions {
  position: absolute;
  color: #fff;
  bottom: 0;
  padding-bottom: 6px;
  font-family: sans-serif;
  width: 100%;
  text-align: center;
  pointer-events: none;
}

background-imageoverflowをコメントアウトすることで背景を消し、スライダーを表示させることができます。

最後に

このスライダーはThree.jsが使われているそうです。
私は、Three.jsは触ったことがないばかりか、HTML,css,JavaScriptについての知識もあまりないので、もしより良い改善策がありましたら、ご教授ください。
また著作権などの権利については、ご自身でお調べになってから使ってくださいね!
最後まで見ていただき、ありがとうございました。

参考にさせていただいたサイト

https://photoshopvip.net/100275
https://codepen.io/zadvorsky/pen/PNXbGo

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

ReactにCarbon Design SystemのUIを導入してみた

はじめに

Carbon Design Systemをあまり聞いたことが無い方が多いと思いますが、IBMのオープンデザインシステムです。
Carbon Design System公式

こういった角張った?特徴的なデザインです。(キャプチャは公式サイトより)
image.png

React、Vue、AngularといったメジャーなUIフレームワークで利用することが出来ます。
今回、関わっているプロジェクトでCarbon Design Systemを使ったUIを作ることになったので導入手順、動作確認までの手順をまとめてます。

環境

  • Windows 10
  • Node.js v12.16.3
  • npm 6.14.4
  • yarn 1.22.4

手順

  • ディレクトリ作成
# アプリのコードを管理するディレクトリを作成
mkdir app
cd app
# クライアントのコードを配置するディレクトリを作成
mkdir client
  • Carbon Design Componentの導入
cd client
npx create-react-app my-app   ※my-app=アプリ名 ※npxはnpm 5.2 から利用できるパッケージランナーツール
cd my-app

yarn add carbon-components carbon-components-reac @carbon/icons-react carbon-icons --save

# scssを使うために必要 バージョンを指定してインストールする
yarn add node-sass@4.14.1 --save
  • client/app-name/ にindex.scssを作成
index.scss
@import 'carbon-components/scss/globals/scss/styles.scss';
  • index.jsをindex.scssを読み込むように修正
index.js
import './index.scss';
  • client/app-name/ App.css -> App.scssにリネーム(せっかくなので。やらなくても良い)

これで準備完了。動作確認をします。
↓を参考にヘッダーを付けてみます。
https://www.carbondesignsystem.com/components/UI-shell-header/usage

  • App.jsを修正
App.js
import React from "react";
import { render } from "react-dom";

import Search20 from "@carbon/icons-react/lib/search/20";
import Notification20 from "@carbon/icons-react/lib/notification/20";
import AppSwitcher20 from "@carbon/icons-react/lib/app-switcher/20";
import {
  Header,
  HeaderName,
  HeaderGlobalAction,
  HeaderGlobalBar,
  HeaderNavigation,
  HeaderMenu,
  HeaderMenuItem
} from "carbon-components-react/lib/components/UIShell";

import { Button } from 'carbon-components-react';

import './App.scss';

function App() {
  return (
    <div className="App">
      <div className="container">
        <Header aria-label="IBM Platform Name">
          <HeaderName href="#" prefix="IBM">
            [Platform]
          </HeaderName>
          <HeaderNavigation aria-label="IBM [Platform]">
            <HeaderMenuItem href="#">Link 1</HeaderMenuItem>
            <HeaderMenuItem href="#">Link 2</HeaderMenuItem>
            <HeaderMenuItem href="#">Link 3</HeaderMenuItem>
            <HeaderMenu aria-label="Link 4" menuLinkName="Link 4">
              <HeaderMenuItem href="#">Sub-link 1</HeaderMenuItem>
              <HeaderMenuItem href="#">Sub-link 2</HeaderMenuItem>
              <HeaderMenuItem href="#">Sub-link 3</HeaderMenuItem>
            </HeaderMenu>
          </HeaderNavigation>
          <HeaderGlobalBar>
            <HeaderGlobalAction aria-label="Search" onClick={() => {}}>
              <Search20 />
            </HeaderGlobalAction>
            <HeaderGlobalAction aria-label="Notifications" onClick={() => {}}>
              <Notification20 />
            </HeaderGlobalAction>
            <HeaderGlobalAction aria-label="App Switcher" onClick={() => {}}>
              <AppSwitcher20 />
            </HeaderGlobalAction>
          </HeaderGlobalBar>
        </Header>
      </div>
    </div>
  );
}

export default App;

このような表示がされれば成功。それっぽい画面になりました。
image.png

参考サイト

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

Next.jsでクエリパラメータを遷移先に渡すにはどうすればいいのか?

Next.jsでクエリパラメータを渡す方法について日本語の記事が少ないので、この記事では「Next.jsでどのようにしてクエリパラメータを渡すのか?」について解説します。

まず、サンプルで作る画面は以下の通りです。
Nextjsのパラメータ引き渡し.gif
大まかに説明するとホーム画面(index.jsx)で入力した内容を出力画面(output.jsx)で出力しています。

ホーム画面

WS000005.JPG

index.jsx
import { useRouter } from 'next/router';
import {useState} from 'react';

export default function Index() {
  const router = useRouter();           //ルーターの取得
  const [input, setInput] = useState();

  // ボタンをクリックしたときの処理
  const clickButton = () => {
    //未入力の時
    if (!input) {
      return;
    }

    router.push({
        pathname:"/output",   //URL
        query: {input :input} //検索クエリ
      });
  }

  return (
    <div style={{textAlign: "center", marginTop: "50px"}}>
      {/* 入力項目 */}
      <input 
        type="text" 
        value={input}
        onChange={(e) => setInput(e.target.value)} /*変更時inputに値をセット*/
      />

      {/* ボタン */}
      <button 
        onClick={clickButton}
        disabled={!input}>    {/*入力項目が未入力の場合、非活性*/}
        遷移
      </button>
    </div>
  )
}

ホーム画面では、入力内容を出力画面に渡して画面遷移を行っています。大きく分けると2ステップです。

まず最初に、useRouterフックを使うためにインポートが必要です。
import { useRouter } from 'next/router';

次にrouterオブジェクトのpushを使って画面遷移を行っています。このとき、クライアントサイドでの遷移となり、サーバーからリクエストが返されるわけではないので注意が必要です。

そして、オプションに以下の設定を行います。
pathname:遷移する先のURL
query:遷移先に渡すパラメータ

これにより「遷移先」、「遷移先に渡すパラメータ」がセットされます。注意点としてはqueryはオブジェクト型で渡すことです。そのため文字列をそのまま渡すのではなく、連想配列で渡します。

今回の場合は、inputに入力した値をセットしています。

出力画面

WS000003.JPG

ouput.jsx
import { useRouter } from 'next/router';

export default function Output() {
  const router = useRouter();

  return (
    <div style={{textAlign: "center", marginTop: "50px"}}>
      {/* パラメータの表示 */}
      <h1>input:{router.query.input}</h1>
    </div>
  )
}

出力画面では渡ってきたパラメータを表示するだけです。

routerオブジェクトのqueryにパラメータが入っているので、router.query.inputで取得できます。

ちなみに、遷移元でパラメータが設定されていない場合、{}で渡ってくるので先ほどの値はundefinedとなり、空白で表示されます。
WS000004.JPG

まとめ

  • useRouterフックを使ってrouter.pushpathname(遷移先)query(渡すパラメータ)をセットする。
  • 遷移先では、router.query.「パラメータ名」で出力できる
  • 呼び出し元でパラメータが設定されていなければ、{}で渡される
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuetifyでもっと早く知りたかったこと

はじめに

プログラミング未経験のわたしが初めて UI framework を利用するにあたって、「もっと早く知っておけば!」と思った情報をまとめておきます。

大前提

v-app の中で使用してください。
公式:Application service

See the Pen vuetify needs v-app by msickpaler (@msickpaler) on CodePen.

Grid System

Vuetify.js 2.2 の GridSystem について
この記事見ればだいたい分かります。特に v-container, v-row, v-col についてはこちらを参照してください。

v-navigation-drawer(サイドバー)

メインコンテンツと被ったとき

その時は、メインコンテンツを v-main で囲ってください(Vuetify 使うと決まったらすぐにやっておくべき)。

See the Pen navigation without <v-main> by msickpaler (@msickpaler) on CodePen.

なんか表示崩れたとき

サイドバーが原因なら、app プロップス使えば直ります。

See the Pen navbar without app props by msickpaler (@msickpaler) on CodePen.

breakpoint

めちゃくちゃ便利。
公式:Display Breakpoints
以下はサイドバーの例で

  • md 以上の大きさで常時表示
  • sm 以下でトグル式
  • xs 以下でトグル式かつ下から表示

が切り替えられます。

See the Pen toggle by display size by msickpaler (@msickpaler) on CodePen.

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

【JS】オブジェクトの非破壊処理の注意点。シャロウコピーとディープコピーの違い。

要素にオブジェクトを含む配列で非破壊処理をしようとしたところ、意図せず破壊処理になってしまった。。

この場合の理由と対処法についてのメモ。


実現したいこと

実現したいのは、以下のように中身は全く同じだが、異なる配列を用意し、指定した配列の要素のみを変更したい。

arr1 = [{a:1,b:2}]
arr2 = [{a:1,b:2}]

arr2[0].a = 999

console.log("arr1:", arr1)  //{a: 1, b: 2}
console.log("arr2:", arr2)  //{a: 999, b: 2}

これを、arr2をarr1を使った形で定義したい。


問題点

非破壊処理の方法として、スプレッド構文concatメソッドを使用することがある。

が、オブジェクトの入った配列の要素をこの方法で変換しようとすると破壊処理になってしまう



▼問題の実例

例えば、以下のように arr1 を非破壊で処理するため、arr2を作り、arr2の0番目のプロパティaに999を代入すると、arr1まで変わってしまう。。

破壊になってしまう
arr1 = [{a:1,b:2}]
arr2 = [...arr1]   //新たな配列を用意

arr2[0].a = 999

console.log(arr1)  //{a: 999, b: 2}
console.log(arr2)  //{a: 999, b: 2}

console.log(arr1) は {a: 1, b: 2}のままにしたい。。


発生原因

なぜこの状態が発生してしまうのか?

スプレッド構文(またはconcatなど)で新たに用意した変数は、それ自体は異なるものになるが、中身のオブジェクトは同じものを指し示す状態となる。

新たな配列の検証
arr1 = [{a:1,b:2}]
arr2 = [...arr1]

console.log(arr1 == arr2)  //false

上記のように、スプレッド構文で新たに作った変数は元と違う(false)になっている。

イメージとしては、 [{ }] という配列の場合、外側の[ ]は新たな変数になるが、内側の{ }は元のままとなっている状態。

これをシャロウコピーという。


シャロウコピーとディープコピーとは?

・シャロウコピー(shallow copy)のshallowは浅いという意味で、外側はコピーしてるが、内側はそのままということを表している。

まさに、上記の[{ }]をスプレッド構文で外側の[ ]は新しい変数になったが、内側の { }は元のプロパティを参照している状態。



・ディープコピー(deep copy)とは名前の通り、shallowよりも深く、外側だけでなく、内側も新しいオブジェクトにした状態のこと。

オブジェクトの入った配列を非破壊で値を変更する場合にはディープコピーにする必要がある。


ディープコピーの方法

deep copyの方法はいくつかあるが、mapとスプレッド構文を使って、配列内のオブジェクトを1つづつ展開していく方法が簡単。

非破壊処理
arr1 = [{a:1,b:2}]
arr2 = arr1.map( obj => ({...obj}) )

arr2[0].a = 999

console.log(arr1)  //{a: 1, b: 2}
console.log(arr2)  //{a: 999, b: 2}

配列.map( 変数名 => 処理 )
・mapは指定した配列の要素を一つづつ取り出す処理(非破壊)。
・処理でオブジェクトを記述する場合は、( )で囲む必要がある。

{...オブジェクト}
スプレッド構文を使うと、外側の{ }または[ ]を展開する。
展開すると元とは異なる、新しい要素になる。


ディープコピーの補足

上記 arr1 のように、配列内のオブジェクトが1つの場合は、mapを使わず直接指定する方法も同じ結果になる。

非破壊処理
arr1 = [{a:1,b:2}]
arr2 = [{...arr1[0}]

arr2[0].a = 999

console.log(arr1)  //{a: 1, b: 2}
console.log(arr2)  //{a: 999, b: 2}

mapで1つづつやっていることを、1つだけ指定して処理したパターン。

mapの裏側の処理をわかりやすくしたもの。汎用性はないので、mapとスプレッド構文の処理方法を覚えておけばOK。

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

Web API フロント側

Web APIを使ってCRUD操作していきます。

CRUDとHTTPメソッドの対応

CRUD HTTPメソッド 意味
Create POST 作成
Reading GET 取得
Update PATCH 更新
Delete DELETE 削除

※PUTは今回扱いません

送信データの形式

送信は3通りの形式がありますが、本記事ではJSON形式で進めます。

  • JSON形式(application/json)
  • フォーム形式(multipart/form-data)
  • application/x-www-form-urlencoded形式

1.コードを書かないで体験してみる

最初はコードを書かずに、json-serverとPostmanを使ってWeb APIでのCRUD操作のシミュレーションをしてみます。

本番 本章
フロント JavaScript Postman
サーバー サーバーサイドの言語(PHP、Pubyなど) json-server

JSONファイルの作成

サンプルとして、文豪と著書のリストをJSONで作成します。
novelsという部分がAPIアクセス時のエンドポイントの文字列になります。
(route.jsonを使うと別の文字列に変更することも可能です。)

任意のフォルダの中にdb.jsonファイルを作成します。

db.json
{
  "novels": [
    {
      "id": 1,
      "title": "坊っちゃん",
      "author": "夏目漱石"
    },
    {
      "id": 2,
      "title": "羅生門",
      "author": "芥川龍之介"
    },
    {
      "id": 3,
      "title": "走れメロス",
      "author": "太宰治"
    },
    {
      "id": 4,
      "title": "雪国",
      "author": "川端康成"
    }
  ]
}

json-server

インストール

terminal
npm install -g json-server

起動

terminal
json-server --watch db.json

動作確認

下記URLにブラウザでアクセスすると、先程作成したdb.jsonの内容が表示されます。
http://localhost:3000/novels
(URLは環境によって異なる場合があります)

Postman

インストール

下記URLからダウンロードして、インストールします。
https://www.postman.com/

起動

インストールができたら起動します。

操作してみる

GET

db.jsonのnovelsの内容を取得してみます。

postman_get.jpg


1.methodにGETを指定
2.URL欄に http://localhost:3000/novels/ を入力
3.「Send」ボタンを押す


db.jsonの内容が表示されます。

POST

db.jsonのnovelsに新規のデータを追加してみます。

postman_post.jpg


1.methodにPOSTを指定
2.URL欄に http://localhost:3000/novels/ を入力


Bodyタブを開いて、rawを選択
入力欄に下記を入力

json
{
  "title": "細雪", 
  "author": "谷崎潤一郎" 
}

「Send」ボタンを押す


登録したデータが表示されます。

DELETE

db.jsonのnovelsから指定したデータを削除してみます。
(今回はID4を削除)

postman_delete.jpg


1.methodにDELETEを指定
2.URL欄に http://localhost:3000/novels/4 を入力
3.「Send」ボタンを押す

データが削除されます。

PATCH

db.jsonのnovelsから指定したデータを更新してみます。
(今回はID1を更新)

postman_patch.jpg


1.methodにPATCHを指定
2.URL欄に http://localhost:3000/novels/1 を入力


Bodyタブを開いて、rawを選択
入力欄に下記を入力

json
{
  "title": "吾輩は猫である"
}

「Send」ボタンを押す


更新したデータが表示されます。
指定していないauthorはそのまま変更されていません。

2.JavaScriptでCRUD

Postmanを使っていた部分をJavaScriptで行っていきます。
(Postmanはもう不要なので終了して良いです。)

db.jsonに変更を加えたので、初期の状態に戻しておいてください。

fetch(Promise、async await)、axiosの3つの方法で操作してみます。

本番 本章
フロント JavaScript JavaScript
サーバー サーバーサイドの言語(PHP、Pubyなど) json-server

fetch(promise)の場合

GET

db.jsonのnovelsの全データを取得してみます。

js
fetch("http://localhost:3000/novels", {
  //取得なのでGETを指定(GETは省略可能)
  method: "GET",
})
.then((res) => {
  //responseオブジェクトからJSONオブジェクトを抽出
  return res.json();
})
.then((json) => {
  //取得できたJSONオブジェクトをJSON文字列化して表示
  console.log(JSON.stringify(json));
});

POST

谷崎潤一郎の「細雪」を追加してみます。

js
//postするJSONオブジェクト作成
const json = { title: "細雪", author: "谷崎潤一郎" };

fetch("http://localhost:3000/novels", {
  //新規追加なのでmethodにPOSTを指定
  method: "POST",

  //bodyにJSONオブジェクトをJSON文字列化して指定
  body: JSON.stringify(json),

  //headersで"Content-Type": "application/json"を指定
  headers: { "Content-Type": "application/json" },
});

DELETE

ID4を指定して、川端康成の「雪国」を削除してみます。

js
fetch("http://localhost:3000/novels/4", {
  //削除なのでmethodにDELETEを指定
  method: "DELETE",
});

PATCH

ID1を指定して、夏目漱石の「坊っちゃん」を、夏目漱石は変更せず「吾輩は猫である」に変更してみます。

js
//PATCHに使うJSONオブジェクト作成
const json = { title: "吾輩は猫である" };

fetch("http://localhost:3000/novels/1", {
  //更新なのでmethodにPATCHを指定(PATCHを小文字にすると動作しないので必ず大文字にする)
  method: "PATCH",

  //bodyにJSONオブジェクトをJSON文字列化して指定
  body: JSON.stringify(json),

  //headersで"Content-Type": "application/json"を指定
  headers: { "Content-Type": "application/json" },
});

fetch(async await)の場合

GET

db.jsonのnovelsの全データを取得してみます。

js
getData = async () => {
  const res = await fetch("http://localhost:3000/novels", {
    method: "GET",
  });

  //responseオブジェクトからJSONオブジェクトを抽出
  const json = await res.json();

  //取得できたJSONオブジェクトをJSON文字列化して表示
  console.log(JSON.stringify(json));
};

getData();

POST

谷崎潤一郎の「細雪」を追加してみます。

js
//postするJSONオブジェクト作成
const json = { title: "細雪", author: "谷崎潤一郎" };

postData = async () => {
  await fetch("http://localhost:3000/novels", {
    //新規追加なのでmethodにPOSTを指定
    method: "POST",

    //bodyにJSONオブジェクトをJSON文字列化して指定
    body: JSON.stringify(json),

    //headersで"Content-Type": "application/json"を指定
    headers: { "Content-Type": "application/json" },
  });
};

postData();

DELETE

ID4を指定して、川端康成の「雪国」を削除してみます。

js
deleteData = async () => {
  await fetch("http://localhost:3000/novels/4", {
    //削除なのでmethodにDELETEを指定
    method: "DELETE",
  });
};

deleteData();

PATCH

ID1を指定して、夏目漱石の「坊っちゃん」を、夏目漱石は変更せず「吾輩は猫である」に変更してみます。

js
//PATCHに使うJSONオブジェクト作成
const json = { title: "吾輩は猫である" };

patchData = async () => {
  await fetch("http://localhost:3000/novels/1", {
    //更新なのでmethodにpatchを指定(PATCHを小文字にすると動作しないので必ず大文字にする)
    method: "PATCH",

    //bodyにJSONオブジェクトをJSON文字列化して指定
    body: JSON.stringify(json),

    //headersで"Content-Type": "application/json"を指定
    headers: { "Content-Type": "application/json" },
  });
};

patchData();

axiosの場合

ライブラリの導入が必要ですが、fetchと違いIE11にも対応可能で、「Body.json()が不要」など、記述も少なくて済みます。

npmでのインストール

terminal
npm install axios

CDNを使用する

html
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

GET

db.jsonのnovelsの全データを取得してみます。

js
axios.get("http://localhost:3000/novels").then((res) => {
  console.log(res.data);
});

POST

谷崎潤一郎の「細雪」を追加してみます。

js
//postに使うJSONオブジェクト作成
const json = { title: "細雪", author: "谷崎潤一郎" };

axios.post("http://localhost:3000/novels", json);

DELETE

ID4を指定して、川端康成の「雪国」を削除してみます。

js
axios.delete("http://localhost:3000/novels/4");

PATCH

ID1を指定して、夏目漱石の「坊っちゃん」を、夏目漱石は変更せず「吾輩は猫である」に変更してみます。

js
//patchするJSONオブジェクト作成
const json = { title: "吾輩は猫である" };

axios.patch("http://localhost:3000/novels/1", json);

3.ブラウザ上の操作でデータを更新できるようにしてみる

本番 本章
フロント JavaScript JavaScript
サーバー サーバーサイドの言語(PHP、Pubyなど) json-server
ファイル構成
 server
 ├ db.json
 ├ package-lock.json
 └ node_modules/

 front
 ├ index.html
 ├ main.js
 └ style.css

サーバー側は前章までと同様json-serverを使います。
フロント側はVS Codeでlive serverを起動するのが簡単だと思います。

画面

初期表示
gui1.png

新規追加
gui2.png

新規追加後
gui3.png

ID4削除
gui4.png

ID4削除後
gui5.png

ID1更新
gui6.png

ID1更新後
gui7.png

リポジトリ

ソースは長くなってしまったのでこちらに置きました。

https://github.com/takeshisakuma/webapi-front

fetch APIのheaders以外のオプション

今回は扱いませんでしたが、fetch APIにはheaders以外のオプションもあります。

modeオプション

リクエストのモードを決めます。

設定値 意味
same-origin 同一オリジンのみ許可
クロスオリジンは接続エラー
cors(デフォルト) クロスオリジンを許可
レスポンスは表示できるヘッダが Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragmaに限定される
no-cors 同一オリジンのみ許可
クロスオリジンは接続ができないがエラーにならない(空のレスポンスが返される)

credentialsオプション

クッキーなどのクレデンシャル(認証情報)を送る場合に使用します。

設定値 意味
omit リクエストにクレデンシャルを含まない
same-origin(デフォルト) 同一オリジンの場合だけクレデンシャルを送る
include クレデンシャルの入ったリクエストを送る

redirectオプション

設定値 意味
manual HTTPリダイレクトには従わないが、res.url が新しい URL になり、res.redirected が true になる
必要に応じて新しい URL へ手動でリダイレクトできる
follow(デフォルト) HTTPリダイレクトに従う
error HTTPリダイレクトの場合にはエラーになる

referrer オプション

現在のオリジン内の任意の Referer を設定するか、それを無効にすることができます。
referer を送らないようにするには、空文字をセットします。

referrerPolicy オプション

Referer に対する一般的なルールを設定します。

設定値 意味
no-referrer Referer ヘッダー全体が省略される(リクエストとともにリファラー情報が送られない)
no-referrer-when-downgrade(デフォルト) セキュリティ水準が低下する場合(HTTPS→HTTP) は、リファラー(URL のオリジン、パス、クエリ文字列)は送信されない
origin 文書のオリジンのみがリファラーとして送信される
origin-when-cross-origin 同一オリジン間ではオリジン、パス、クエリ文字列を送信する クロスオリジンの場合は文書のオリジンのみ送信
same-origin 同一オリジンにはリファラーが送信されるが、クロスオリジンの場合はリファラー情報が送信されない
strict-origin セキュリティ水準が低下する場合(HTTPS→HTTP) は、リファラー(URLのオリジン)は送信されない
strict-origin-when-cross-origin 同一オリジン間でリクエストを行う場合はオリジン、パス、クエリ文字列を送信
クロスオリジンの場合、プロトコルのセキュリティ水準が同じ場合はオリジンを送信するが、安全性の劣る送信先 (HTTPS→HTTP) にはヘッダーを送信しない
unsafe-url セキュリティに関係なく、どのリクエストを行った場合でも、オリジン、パス、クエリ文字列を送信

cacheオプション

HTTP キャッシュの使い方を調整できます。

設定値 意味
default(デフォルト) 標準の HTTP キャッシュのルールとヘッダを使用
no-cache キャッシュされているレスポンスがある場合は条件付きリクエストを作成
無い場合は通常のリクエストを作成(レスポンスで HTTP キャッシュを埋める)
reload HTTPキャッシュから結果を取得しないが、キャッシュにレスポンスを埋める(レスポンスヘッダが許可している場合)
force-cache HTTPキャッシュからのレスポンスを使用 HTTPキャッシュにレスポンスが無い場合は、通常の HTTP リクエストを行い通常通りの振る舞いをする
only-if-cached HTTPキャッシュからのレスポンスを使用 HTTPキャッシュにレスポンスが無い場合は、エラー(mode が "same-origin" の場合にのみ動作)
no-store HTTPキャッシュを完全に無視

※下記のヘッダを設定している場合は、no-storeがデフォルト値になります。

  • If-Modified-Since
  • If-None-Match
  • If-Unmodified-Since
  • If-Match If-Range

integrity オプション

レスポンスが既知のチェックサムに一致するかを確認することができます。

サポートされているハッシュ関数

  • SHA-256
  • SHA-384
  • SHA-512

keepalive オプション

リクエストがページよりも長生きする可能性があること意味します。

設定値 意味
true 有効
false 無効
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】React Storybook v.6 導入 サンプル

はじめに

"React Storybook" の導入について簡単にまとめたメモとして残します。


Storybook とは

Storybook を使用することで、UI コンポーネントをビジネスロジックやコンテキストから切り離して開発できるようになります。また可視化することができるので提案、打ち合わせなどにも活用できます。


はじめ方

さっそくですがまずは、Reactのアプリケーションを作成します。

npx create-react-app React-Story-Book


RCAで作成したアプリにStorybookを追加します。

npx -p @storybook/cli sb init

コマンドの実行には基本的に yarn となりますが、 yarn ではなく npm を使用したい場合には、上記のコマンドの末尾に--use-npmつけることで、npmコマンドを使用することができます。
(今回は--use-npmをつけてnpmで実行しました)


処理が完了したら下記コマンドを実行します。

npm run storybook

これでポート 9009でStorybookのデフォルトのエクスプローラーが表示されたと思います?


簡単なボタンの作成

最後に簡単なボタンコンポーネントの例を作っていきたいと思います。

準備

  • 上記コマンドによりjsonファイルに各Storybookの追加された記述と
    アプリケーションフォルダ内にstorybookディレクトリとsrcディレクトリ直下にstoryディレクトリが作成されているのが確認してください。

  • 今回は新たにディレクトリを作成していくので、src直下のstoryディレクトリを削除します。

  • srcディレクトリ直下にcomponentsディレクトリを作成してさらにButtonディレクトリを作成し、その中に以下3つのファイルを作成します。

 ・Button.js
 ・Button.stories.js
 ・Button.css
ディレクトリ


ソースコード

Button.js

Button.js
import React from 'react';
import './Button.css'

function Button(props) {
  const { variant = 'primary', children, ...rest} = props
  return (
    <button className={`button ${variant}`} {...rest} >
      { children }
    </button>
  )
}

export default Button

Button.css

Button.css
.button {
  border: none;
  color: white;
  padding: 15px 32px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  border-radius: 4px;
  cursor: pointer;
}

.primary {background-color: #008CBA;}
.secondary {background-color: #e7e7e7; color: black;}
.succcess {background-color: #4CAF50;}
.danger {background-color: #f44336;}

Button.stories.js

Button.stories.js
import React from 'react';
import Button from './Button';


export default {
  title: 'Button',
  component: Button
}

export const Primary = () => <Button variant='primary'>Primary</Button>
export const Secondary = () => <Button variant='secondary'>Secondary</Button>
export const Succcess = () => <Button variant='succcess'>Succcess</Button>
export const Danger = () => <Button variant='danger'>Danger</Button>


上記ソースコードを実際に出力するとこのようになると思います。

storybook

  • Button.jsで左のサイドバーの親となるButtonの作成。
  • Button.stories.jsでその子となるコンポーネントを記述。
  • Button.cssでそれぞれの見た目を記述。


終わりに

簡単な導入方法と1例の紹介でした!
さらに掘り下げる場合は公式サイトなどを見てみると良いと思います!

Storybook の公式ドキュメント

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

【Nuxt.js】Nuxt実践編:パンくずリストを作ろう

? この記事はWP専用です
https://wp.me/pc9NHC-S8

前置き

スクリーンショット-2020-11-16-20.01.56.png

今回はパンくずリストを作成します?
すごくシンプルに、
コンポーネント作成のコツを
簡単に理解できるので
ぜひやってみてください✨?

構成

アトミックデザイン

⬇️アトミックデザインや
分け方についてはこちら

他にもいくつか書いているので
ブログ内で検索してみてください?

パンくずリストはナビの役割なので
templatesに入れています??

また、パンくずリストは
ヘッダーに入れたりするのが基本ですが、
今回は表示ができれば良いので
index.vueでimportしてもらえればOKです?‍♀️

file
components/
--| templates/
----| navs
------| NavBread.vue

pages/
--| index.vue

使用するもの

CSS

NavBread.vue
fontsize: 12px;
color: rgba(0, 134, 231, 0.8);

paddingは大体で良いです?

⬇️横並びはFlexboxにしましょう!
【Nuxt.js】Nuxt実践編:Flexboxを使おう!

template

ABOUTページを開いている状態で、
TOPへのリンクをしてください?

また、画像はSVGを使用してみましょう!
できる方はSVGコンポーネントを作成しましょう?‍♀️
https://iconmonstr.com/arrow-25-svg/

Let's try!

まずは答えを見ずにやってみましょう♪

答え

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-S8

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

【Nuxt.js】Nuxt実践編:Flexboxを使おう!

? この記事はWP専用です
https://wp.me/pc9NHC-TP

前置き

Frame-15 (1).png

CSSでよく使う
flexプロパティを使ってみましょう?
横並びや中央揃えが
簡単にできるものですね?

時と場合によりますが
floatより便利で高頻度で使います?

⬇️こんな感じで使います❣️

index.vue
display: flex;
justify-content: space-between;
align-items: center;

MDN: CSS フレックスボックスレイアウト

FlexboxとはFlexible Box Layout Moduleのことで、
その名の通りフレキシブルで簡単にレイアウトが組めちゃう素敵ボックスです。
日本語対応!CSS Flexboxのチートシートを作ったので配布します

今までNuxtメインで
肝心のHTML,CSSを
あまりやっていなかったので
少しずつやっていきます??‍♀️

作る物

Frame-16.png

前置きにあった画像の
カード部分です?

ネイルデザインの
カードをイメージしてます?❤️
四角◾️には画像が入ります。
ネイルの名前と画像、
紹介文とタグ、
ネイリストの他の作品も一覧で見る?
といった感じにしています。

⬇️リンク先
ボタンを押すとネイリストのネイル一覧
同じくカードを押すしてもネイル一覧
タグを押すとタグに該当するデザインの一覧

構成

では構成を考えていきましょう??
まずはディレクトリ 、コンポーネントから。

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-TP

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

【動画版】if (hoge) {~ 比較演算子を使わないIF文がどこまでを「true」とするのかを徹底的に検証してみた*JavaScript

前提

if (text) {
  return true
} return false

 
 
この書き方が

どの時が「true」で

どの時に「false」を返すのか

を検証。
 
 
そして、動画にしてみた記事である。

 

 

 
*********************
前回の記事はテキスト版として掲載している。
動画で分かりづらかったらコチラ

→前回の記事 テキスト版
 
 
 
 

検証環境

・フレームワーク:React.js

※少なくともJavaScript系の言語は同じ結果だと思う。
 
 
・コード

// この変数にいろんな物を入れてみることで検証
let text = 'hoge'

const result = () => {
  // ↓ここの判定具合を検証する
  if (text) {
    console.log('text:', text, true) // 条件一致
    return text + ' ←textの中身'
  }
  console.log('text:', text, false) // 条件不一致
  return 'textになんも入ってないで'
}

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>{result()}</p> {/* ←ここで画面に表示 */}
      </header>
      <div></div>
    </div>
  );
}

export default App;

  
 

では今回は動画で検証開始!!!!!!!!

 

 
 
 
 
※音声あるのでご注意を




 
 
 
 
 
 
 

まとめ

動画作るのは結構エネルギー使うので、
たまにでいいと思った。
 
 
次回はテキスト版で、
「length」の検証を書こうかな。
 
 

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

vue.js 小数点が省略されたが、表示させたい!(例えば、10->10.0で表示したい)

jsは整数の場合、小数が自動的に省略されたことがついに気づきました。

例えば、10ではなく、10.0で画面で表示したい場合、
postmanでPrettyだと、10で表示しています。「あれ?」と思って、Rawで確認すると、10.0になっています。

vue.jsでどうやって10.0で表示できるの?
色々調べて、下記のコードでできた!

// 負でない整数場合、小数一桁「0]を補足する
var getFloatStr = function (num) {
        if (!/\./.test(num)) {
          num += '.0'
        }
        num = num.match(/\d+\.\d{1}/)[0]
        // console.log(num)
        return num
      }

スクリーンショット 2020-11-27 13.27.04.png

コードの全文

<template>
    <b-card header="結果" bg-variant="light" class="text-center" style="margin-bottom:10px;">
       <b-card-text>
          <div>
          <input style="display:none" type="text" v-model="childrenMessage">
            <element-table
              ref="table"
              :data="data"
              :columns="columns"
              />
            </div>
      </b-card-text>
    </b-card>
</template>
<script>

export default {
  props: ['message'],
  data () {
    return {
      childrenMessage: '',
      columns: [
        [
          {
            title: '順位',
            field: 'ranking'
          },
          {
            title: '点数',
            field: 'score'
          },
          {
            title: '名前',
            field: 'name'
          },
          {
            title: 'メモ',
            field: 'memo'
          }
        ]
      ],
      data: []
    }
  },
  created () {
    this.childrenMessage = this.message
  },
  watch: {
    message (val) {
      const result = this.message

      const arrResult = []
      const objResult = {}

      // 負でない整数場合、小数一桁「0]を補足する
      var getFloatStr = function (num) {
        if (!/\./.test(num)) {
          num += '.0'
        }
        num = num.match(/\d+\.\d{1}/)[0]
        // console.log(num)
        return num
      }
      for (var i = 0; i < result.length; i++) {
        objResult['ranking'] = result[i]['ranking']
        objResult['score'] = getFloatStr(JSON.stringify(result['score']))
        objResult['name'] = result[i]['name']
        objResult['memo'] = result[i]['memo']
        arrResult.push(objResult)
      }
      this.data = arrResult
    }
  }
}
</script>

参考先:
1、js 输入int类型数字后自动在后面加.00,输入double型保留小数点后两位

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

jQueryを使ってスクーロル時にフワッと文字が浮き上がって出るやつ

はじめに

今回はタイトルにある通りスクロール時にフワッと要素が浮き上がってくる機能を実装したので備忘録として記録していきます。
今回はjQueryを使っての実装になるのでそちらの準備をしてから実装に入って下さい
私自信プログラミング初心者なので間違いなどあればご指摘の程よろしくお願いします!

ステップ1 フワッとさせたい要素を作る

  <div class="container">
  </div>

  <div class="container2">
    <h1>フワッと現れる</h1>
  </div>
.container {
  height: 100vh;
  width: 100%;
  background-color: aquamarine;
}

.container2 {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  width: 100%;
  background-color: rgb(147, 147, 197);
}

こちらはなんの変哲もないhtmlとcssです。
h1タグの部分をスクロールと共に出現するように実装していきます!

ステップ2 JavaScriptの記述

基本的にスクロールに応じてアニメーションを付けるためのJsの流れは
1、ブラウザの表示できる領域の高さを取得
2、スクロール位置を取得
3、アニメーションをさせたい要素のY軸(縦)の位置を取得
4、アニメーションさせる要素かどうかを判断する
5、アニメーションをさせる要素だった場合、クラス.fadeInを追加

これらをコードにすると

$(function () {
  $(window).scroll(function () {
      const wHeight = $(window).height();
      const scrollPosition = $(window).scrollTop();
      $('.scroll').each(function () {
          const targetPosition = $(this).offset().top;
          if(scrollPosition > targetPosition - wHeight + 60) {
              $(this).addClass("fadeIn");
          }
      });
  });
});

まず1、2行目でスクロール時のアクションを定義します。
3行目でブラウザ表示領域の高さをconst wHeightに代入しています。
4行目でスクロール位置の高さをscrollPositionに代入しています。
5行目でクラスscrollが付与されてる要素を繰り返し処理を行っています。
6行目でアニメーションを行いたい要素の高さを取得しています。
7行目でif文でスクロール位置 > 対象となる要素の縦位置 – 表示領域の高さ + 60を定義
ここで+60などを指定しないと画面の下部でアニメーションが行われていて確認が難しくなるためです。
なのでここの値はお好みの値を指定して下さい。
8行目で条件式を満たした場合クラスfeadInを追加してます。

ステップ3 cssでアニメーションを定義する

/*1.フェードインアニメーションの指定*/
.scroll {opacity: 0;} /*一瞬表示されるのを防ぐ*/
.fadeIn {
    animation-name: fadeIn;
    animation-duration: 5s;
    animation-fill-mode: forwards;
}
@keyframes fadeIn {
    0% {
        opacity: 0;         
    }
    100% {
    opacity: 1;
    transform: translate(0);
    }
}

cssで実際にアニメーションを作っていきます。
まずfeadInクラスの説明をします
こちらでアニメーションの名前やかかる時間などの設定をしていきます!
animation-nameでアニメーションの名前を定義しています。
animation-duration: 5s;でアニメーションにかかる時間を設定しています。
animation-fill-mode: forwards;でアニメーション後そのままの状態を維持する記述です。

次に@keyframesでアニメーションの挙動を設定していきます
@keyframesのあとは上で設定したアニメーションの名前を記述して下さい。
実際にこの部分の記述でどのように要素を変化させるかを設定していきます。
今回は0%の時と100%の時の要素の状態を定義していますが、50%や75%など細かく設定する事ができるのでお好みで設定してみて下さい。

これで一通りの準備ができましたので挙動を確認してみましょう!

https://gyazo.com/c8e8aa5f59ec2226d79fcb479c9ed6f6

最後に

僕自身フロント実装は苦手なのですが実際アニメーションを作ってみるととても楽しいですし、モダンなサイト作成には必須な技術かと思うので、今後も少しずつ扱えるようにしたいと思いました!!
まだまだプログラミング初心者のためご指摘などあればよろしくお願いします!

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

【Nuxt.js】Nuxt番外編:
アトミックデザインにおけるフォルダの分け方と
サンプル集

? この記事はWP専用です
https://wp.me/pc9NHC-MQ

前置き

コンポーネントの分類の仕方についてと
少しですがサンプルコード集です??

この要素はどこ❓
molecules❓
それともorganisms❓
なんて時にチェックしてください✅?

フォルダ分けはアトミックデザインを推奨しています。
Atomic Designとは

サンプルコードもあるので
コンポーネントの命名や
中身の構成などの参考にも
お役立てください❤️

コードを書き始める前のチェックリストを
見ておくと尚良いと思います✨?

分け方

アトミックデザインに基づく分け方

アトミックデザイン は、
要素の大きさや機能ごとに
ファイルを分けるやり方です✨?

それに乗っ取って分けていますが、
確実な正解・ルールは存在しないので
あくまでも私たちの分け方として
参考にしてくださいね♪

それぞれ、
どの階層のコンポーネントを
読み込んでもOKです?‍♀️

atoms

UIの最小要素。
button, icon, inputなど。

タイトルのh1と、
サブタイトルのpでセットの場合なんかも
1つのまとまりなのでatomsへ?

molecules

atomsを2, 3つ貼り付けたような物?
ul > li のliや
formに入れるlabelつきのinputなど。

organismがformやulなどの
まとまりなので
それを分解した要素です?

InputDefault.vueを
atomで作っていた場合はimportし、
作っていない場合は
FormItemInput.vueで
直接inputを使ってもOKです⭕️

organisms

formやulなど、
ある程度のまとまり。
modalの中身もココ?

templates

modalやnav, Header, Footer
sectionなどの大きなまとまり?

atoms

スクリーンショット-2020-10-23-9.30.02.png

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-MQ

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

【Nuxt.js】Vue Router基礎編:params, queryを使ってページ遷移

? この記事はWP専用です
https://wp.me/pc9NHC-eI

前置き

とっても便利なparams, queryについてご紹介?
同じコンポーネントを見せたいけど、
カテゴリごとにURLだけを変えたい…
一覧ページからソートして表示させたい…
そんな時に便利です♪

params, queryについて
いくつかに分けて書きます✍️
router-linkが分かれば簡単です?
まだ不安な方のためにも
複数の書き方で記載しました?

params, queryの違い

まずはURLを見るのが
分かりやすいと思います?

localhost:3000/param/param?query=123

パスパラメーター(param)

?より前の部分、省略できない

クエリパラメーター(query)

?以降の部分、省略できる

directoryとの関係①

localhost:3000/project123
projectごとにURLを変更
表示ページは同じでコンポーネントで表示分け

file
pages/
--| _id/
-----| index.vue

directoryとの関係②

localhost:3000/events?today=true
events/index.vueの中で
today=trueでソートをかけて表示

file
pages/
--| events/
-----| index.vue

eventsは絶対省略できないですね。
pages/events/index.vueに
行けなくなってしまいます。

?today=trueは省略しても
ソートが外れるだけなので
ページはきちんと表示されます♪

メリット

? 続きはWPでご覧ください?
https://wp.me/pc9NHC-eI

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

【kintone】アプリの「スペース」フィールドにボタンを設置する

アプリにボタンを設置したいなぁと思うことがあります。よね?
今回はボタンをアプリのスペースフィールドに追加して使えるようにするというお話です✨✨
kintone & JavaScriptカスタマイズを始めたばかりの方向けに書きました^0^

今回つくるボタンの完成イメージ
自作のボタンをクリックすると「プッシュ」と書かれたアラートを表示します。

スペースフィールドの準備

ではまずアプリの準備・・・というかスペースフィールドの準備をします。

(1) スペースフィールドを設置します

(2)要素IDを設定します。今回は spSuteki としておきます。(何でもOKです)

JavaScriptを書く

ボタンを設置するJavaScriptを書きます。
これのコピペで、新規追加画面&編集画面でボタンをひとまず使えるようになります。
解説は後述します。

//新規追加画面でボタンを使いたいときは'app.record.create.show'
//編集画面でボタンを使いたいときは'app.record.edit.show'
kintone.events.on(['app.record.create.show', 'app.record.edit.show'], event => {
    //ボタンを置きたいスペースフィールドを取ってくる
    const sp = kintone.app.record.getSpaceElement('spSuteki');

    //ボタンを作る
    const btn = document.createElement('button');
    //ボタンに表示したいテキスト
    btn.textContent='ボタン';

    //スペースフィールドにボタンを追加する
    sp.appendChild(btn);

    //ボタンをクリックしたときの動作
    btn.onclick=()=>{
        //ここにボタンをクリックしたときの動作を書く
        alert('プッシュ');
    }

    return event;
});    

JavaScript解説

イベントが発生したときに動き出すプログラム

最初のこんな行ですが・・・
kintone.events.on(['app.record.create.show', 'app.record.edit.show'], event =>{~~

「〇〇なイベントが発生したら、~~するよ!」という意味の部分です。
'app.record.create.show' は、「レコード追加画面が表示されるというイベントが発生したら」
'app.record.edit.show' は、「レコード編集画面が表示されるというイベントが発生したら」
です。

event => { の後に、「~~する」をプログラミングします。

スペースフィールドにボタンを置く

画面が表示されるというイベントが発生したら、ボタンを表示したい。
ボタンをスペースフィールドに置きたいのですが、それをプログラミングするには

  1. 「スペース」フィールドをプログラムで扱えるようにする
  2. 「ボタン」を作る
  3. 「スペース」フィールドに「ボタン」を置く

という手順が必要になります。

プログラムを見てみましょう。

1.「スペース」フィールドは↓のようにプログラムで扱えるようにできます。
const sp = kintone.app.record.getSpaceElement('スペースフィールドの要素ID')

アプリの準備のときに、スペースフィールドの要素IDを spSuteki としていました。
下記のようにすることで、spという入れ物に要素IDが spSuteki のスペースフィールドを入れる事ができます。

//ボタンを置きたいスペースフィールドを取ってくる
const sp = kintone.app.record.getSpaceElement('spSuteki');

2.次はボタンを作ります。

JavaScriptで「ボタン(button)」を作るときは document.CreateElement('button') とします。
ボタンに表示したいテキストも設定しておきましょう。

//ボタンを作る
const btn = document.createElement('button');
//ボタンに表示したいテキスト
btn.textContent='ボタン';

3.そして、作ったボタンをスペースフィールドに乗せ(追加し)ます。
sp(スペースフィールド)に、btn(ボタン)を、appendChild(追加)しています。

//スペースフィールドにボタンを追加する
sp.appendChild(btn);

ボタンの動きを作る

ボタン作成の次には、ボタンを押したときにどうするか!?の部分をプログラミングします。
今回はalert()を使って、アラート表示してみましょう。

btn(ボタン)を、onClick(クリックした時) というイメージ↓のように書きます。

//ボタンをクリックしたときの動作
btn.onclick=()=>{
    //ここにボタンをクリックしたときの動作を書く
    alert('プッシュ');//アラート表示
}

まとめ

kintoneではボタンを追加したいときはJavaScriptでカスタマイズする必要があります。
でも、たった何行かのプログラムを書くことでボタンを表示することができると、なんだかできそうな気がしませんか!?

次回は、ボタンを使ってフィールドの値を変更してみるようなプログラミングに挑戦してみたいと思います✨✨
ではでは~

※公式のチュートリアルでもボタンを設置する方法が紹介されていますので、是非そちらもご覧ください^0^
参考:第3回 レコード詳細にもボタンを設置しよう!

以上です✨✨

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

JavaScriptのプリミティブ型について

はじめに

脱初心者にむけてアウトプットをしていこうと思って記事を書いております。
間違ったことがありましたら、ぜひコメントいただけると幸いです。

JavaScriptでは、値の種類が2種類ある

一つはプリミティブ(primitive)、もう一つはオブジェクト(object)である。(今回はプリミティブだけの記事です)

プリミティブは「不変(immutable)」

どういう意味かというと実は簡単で、数値の9は常に9であるということ、文字列の"world"は常に文字列の"world"であること。
文字列"hello" + "world"が連結したら"hello world"になり全く新しい文字列になるのです

ただし、「不変(immutable)」とは変数の内容を変更できないという意味ではないことです。

let str = "hello";
str = "world";

このような文があったとして、
変数strは値"hello"で初期化され、次に新しい不変の値"world"が代入されている。ここで重要なのはhelloとworldは別の値ということ。

プリミティブを表すデータの型

数値(Number) 
文字列(String)
論理値(Boolean)
null
undefined
シンボル(Symbol)

まとめ

JavaScriptのプリミティブについて紹介してきました。
次回はレンプレートリテラルについて記事を書いていこうとおもいます。
ご覧いただきありがとうございました。

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

EJSでもconsole.logが使える。デバッグに便利。

EJSでも、JavaScriptでおなじみのconsole.log()を使うことで実行環境へログを出力することができます。

<% 
    const person = 'taro';
    console.log(person); 
%>
taro

使用しているNode.jsがv10が以上であればconsole.table()なども使うことができます。

<% 
    const people = [
      {name: 'taro', age: 66},
      {name: 'shota', age: 33},
      {name: 'hiroto', age: 10},
    ];
    console.table(people); 
%>
┌─────────┬──────────┬─────┐
│ (index) │   name   │ age │
├─────────┼──────────┼─────┤
│    0    │  'taro'  │ 66  │
│    1    │ 'shota'  │ 33  │
│    2    │ 'hiroto' │ 10  │
└─────────┴──────────┴─────┘

混みいったコードを書く時にデバッグとして使うと便利です。

参考記事

Node.js v10.0.0でconsole.table()追加&console.log()アップデートに感動したので早速試してみる

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

maphilight.jsに点滅機能を追加する方法

maphilight.js」は、クリッカブルマップのエリア要素をハイライトできる便利なライブラリです。

基本的な使い方をお探しの方は、以下を参考にして下さい。
クリッカブル・マップ(usermap)を視覚化する - りきえる’s ブログ

課題

このライブラリ、残念ながらエリアやアウトラインを点滅させることまでは出来ません。
Githubのissuesでも要望は出ていますが、対応する気配は今のところありません:sob:

結論

点滅できました:metal:

Image from Gyazo

方法

・maphilightのopacityオプションの値をsetIntervalで増減させる。
・値の増加と減少の切り替えには、排他制御を利用する。

手順

①htmlファイルにscriptタグを追加

新たにjsファイルを作成します。
ファイル名は、「jquery.maphilight-custom.js」としました。
maphilight.jsの下に配置しましょう。

index.html
<head>
  ...
  <script src="./src/js/jquery.maphilight.min.js"></script>
  <script src="./src/js/jquery.maphilight-custom.js"></script>
</head>

②カスタマイズファイルを作成

新規作成した「jquery.maphilight-custom.js」に以下をコピペして下さい。
個別の解説はあとでしていきます。

jquery.maphilight-custom.js
// maphilightの設定
let options = {
  fill: true, // エリアを塗りつぶす
  fillColor: '000000', // 塗りつぶすカラーコード
  fillOpacity: 1, // 塗りつぶしの透過度(0=透明、1=完全な塗りつぶし)
  stroke: true, // エリアを枠線で囲む(true)
  strokeColor: 'ff0000', // 枠線のカラーコード
  strokeOpacity: 1, // 枠線の透過度
  strokeWidth: 1, // 枠線の太さ
  fade: true,
  alwaysOn: true, // エリアの表示タイミング(true=常に表示、false=オンマウス時のみ表示)
  neverOn: false,
  groupBy: false,
  wrapClass: true,
  shadow: false,
  shadowX: 0,
  shadowY: 0,
  shadowRadius: 6,
  shadowColor: '000000',
  shadowOpacity: 0.8,
  shadowPosition: 'outside',
  shadowFrom: false,

  // 独自に実装したオプション
  fillBlink: false, // エリアを点滅させる(true=点滅させる、false=点滅させない)
  strokeBlink: true, // 枠線を点滅させる(true=点滅させる、false=点滅させない)
  blinkInterval: 1000 // 点滅させる間隔(単位はms)
}

$(function () {
  $('img[usemap]').maphilight(options)

  if (options.strokeBlink || options.fillBlink) {
    const interval = options.blinkInterval / 10
    setInterval(function () {
      blinking()
    }, interval)
  }
})

let mutex = 0

const fill = {
  limit: options.fillOpacity,
  value: function () {
    return this.limit / (options.blinkInterval / 100)
  },
  up: 0,
  down: 0
}

const stroke = {
  limit: options.strokeOpacity,
  value: function () {
    return this.limit / (options.blinkInterval / 100)
  },
  up: 0,
  down: 0
}

function blinking() {
  if (mutex == 0) {
    const data = $('.hilight').mouseout().data('maphilight') || {}
    if (options.strokeBlink) data.strokeOpacity = stroke.up
    if (options.fillBlink) data.fillOpacity = fill.up
    $('.hilight').data('maphilight', data).trigger('alwaysOn.maphilight')
    fill.up += fill.value()
    stroke.up += stroke.value()
    if (
      (options.fillBlink && fill.up >= fill.limit) ||
      (options.strokeBlink && stroke.up >= stroke.limit)
    ) {
      mutex = 1
      fill.down = fill.limit
      stroke.down = stroke.limit
    }
  } else {
    const data = $('.hilight').mouseout().data('maphilight') || {}
    if (options.fillBlink) data.fillOpacity = fill.down
    if (options.strokeBlink) data.strokeOpacity = stroke.down
    $('.hilight').data('maphilight', data).trigger('alwaysOn.maphilight')
    fill.down -= fill.value()
    stroke.down -= stroke.value()
    if (
      (options.fillBlink && fill.down <= 0) ||
      (options.strokeBlink && stroke.down <= 0)
    ) {
      mutex = 0
      fill.up = 0
      stroke.up = 0
    }
  }
}

③利用方法

以下の3つのオプションを調整してご利用下さい。

  // 独自に実装したオプション
  fillBlink: false, // エリアを点滅させる(true=点滅させる、false=点滅させない)
  strokeBlink: true, // 枠線を点滅させる(true=点滅させる、false=点滅させない)
  blinkInterval: 1000 // 点滅させる間隔(単位はms)

お疲れさまでしたー:grin:

個別解説(興味があればでOK)

DOM生成後の処理

DOM生成後にmaphilight.js本体を発火させ、インターバル制御を開始させています。

$(function () {
  $('img[usemap]').maphilight(options)  // maphilight.jsを発火させています

  // 点滅オプションのどちらかがtrueのとき、関数blinkingが動作します
  if (options.strokeBlink || options.fillBlink) {
    // 点滅オプションで設定した時間を10分割して、徐々に透明度を増減させます
    const interval = options.blinkInterval / 10
    // setIntervalメソッドで関数blinkingを繰り返し実行しています
    setInterval(function () {
      blinking()
    }, interval)
  }
})

記述方法の補足です。

$(function () {...}) // この書き方は
$("document").ready(function () {...}) // これの略です

パラメータの定義

エリアと枠線それぞれの、透明度の上限値と増減値を定義しています。

// エリアの点滅に必要な値をまとめたオブジェクトを作成します
const fill = {
  // 透明度の上限値です
  limit: options.fillOpacity,
  // 透明度の増減値です
  value: function () {
    return this.limit / (options.blinkInterval / 100)
  },
  // 増加中の透明度を記録します
  up: 0,
  // 減少中の透明度を記録します
  down: 0
}

// アウトラインの点滅に必要な値をまとめたオブジェクトを作成します
const stroke = {
  limit: options.strokeOpacity,
  value: function () {
    return this.limit / (options.blinkInterval / 100)
  },
  up: 0,
  down: 0
}

this.limitを使用しているvalueプロパティですが、プリミティブ値ではなく関数を使用しないと
thisの参照先がグローバルオブジェクトとなり、undefinedになってしまうので注意して下さい。

const fill = {
  ...
  // OK
  value: function () {
    return this.limit / (options.blinkInterval / 100) // => 例)0.1
  },

  // NG
  value: this.limit / (options.blinkInterval / 100), // => undefined
  ...
}

排他制御によるパラメータの増減処理

今回、透明度を増加させるか減少させるかを排他制御で解決しています。
排他制御(mutex・ミューテックス)というのは、「ダブルブッキングしないように制御すること」です。
今回は、「増加という処理」と「減少という処理」がダブルブッキングしないように制御しています。
具体的には、変数mutexが0のときは増加、1のときは減少させています。

参照: 排他制御とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

// mutexが0なら透明度を増加させ、1なら減少させます
let mutex = 0
...
function blinking() {
  if (mutex == 0) {
    ... // 透明度を増加させる処理
    if (
      // 透明度が上限に達した場合、mutexを1にして減少させる処理を開始します
      (options.fillBlink && fill.up >= fill.limit) ||
      (options.strokeBlink && stroke.up >= stroke.limit)
    ) {
      mutex = 1
      ...
    }
  } else {
    ... // 透明度を減少させる処理
    if (
      // 透明度が0に達した場合、mutexを0にして増加させる処理を開始します
      (options.fillBlink && fill.down <= 0) ||
      (options.strokeBlink && stroke.down <= 0)
    ) {
      mutex = 0
      ...
    }
  }
...


ありがとうございました!以上です。

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

Python学習 基礎編 ~コメントアウトとは?~

こちらではPython学習の備忘録と、Ruby、JavaScriptとの比較も含め記載していきたいと思いましたが、Pythonの説明が意外と長くなってしまったので今回はPythonのみとします。
プログラミング初心者や他の言語にも興味、関心をお持ちの方の参考になれば幸いです。

コメントアウトとは?

「コメントアウトした内容は実行プログラムから無視される」という特徴があります。
つまり、コードに関するメモや意味を、プログラムに影響を与えることなく残せるので便利です。
また、この特徴を活かして、コードを試行錯誤したい時にコメントアウトが役立ってくれます。


コメントを記述するには?

Python

  • 1行コメント

「#」から行末までは実行時に無視される。
「#」より前のコードは有効。
上記より範囲指定はできない。

script.py
# この行はコメントです。
a = "コメント"
print(a) # 変数aを出力
  • トリプルクォートによる複数行コメント

関数(defブロック)やクラスなどの先頭の文字列に説明を記述するdocstring(ドキュメンテーション文字列)という仕組みがある。
【参考記事】
Pythonのdocstring(ドキュメンテーション文字列)の書き方

script.py
def test(a, b):
    '''docstring
    description
    '''
    print(a)
    print(b)

シングルクォート「'」、 またはダブルクォート「"」を3つ繋げたトリプルクォート「'''または"""」で囲むことで改行を含めた文字列を生成することができるため、docstringではトリプルクォートが使われるいことが多い。
【参考記事】
Pythonで文字列生成(引用符、strコンストラクタ)

文字列を単独で記述してもコードの処理に影響を与えないのでdocstring以外でもトリプルクォートが複数行のコメントやコメントアウトとして利用されている場合がある。

script.py
a = 1
'''
b = 2
c = 3
d = 4
'''
e = 5

トリプルクォートはあくまでも文字列なので、「#」によるコメントのように実行時に無視されるわけではない。
そのため、インデントされているブロックの中でトリプルクォートをコメントとして利用する場合、インデントを合わせないとエラーになる。

script.py
# インデントが合っているのでエラーにならない
def test(a, b):
    print(a)
    '''
    comment line1
    comment line2
    comment line3
    '''
    print(b)
script.py
# インデントが合っていないのでエラー
def test(a, b):
    print(a)
'''
comment line1
comment line2
comment line3
'''
    print(b)

# IndentationError: unexpected indent

「#」によるコメントは実行時に無視されるのでインデントが合っていなくてもエラーにはならないが、コードとしての可読性を良くするにはインデントを合わせた方が良い。

script.py
def test(a, b):
    print(a)
    # comment line1
    # comment line2
    # comment line3
    print(b)

def test(a, b):
    print(a)
# comment line1
# comment line2
# comment line3
    print(b)

まとめ

いずれにせよトリプルクォートについては、コメントではなく文字列なのでdocstring以外では使わない方が無難だと思われます。
Python3.0以降では、関数アノテーションというものがあるらしいが、こちらはまた後日。


【参考記事】
大変勉強になりました、ありがとうございます。
Pythonのコメント、コメントアウトの書き方

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