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

【javascript】モジュール

モジュールとは

  • 最も基本的なものは独自のスコープとルールを持つ1つのjavascriptファイル
  • モジュールはオブジェクトではない
  • モジュールにデータ型はない
  • モジュールを変数に代入できない
  • コードの分割、カプセル化、構成をするためのツール

モジュールを利用すれば必要なものをインポートし、認知不可を抑え、メンテを楽にしてくれる。

モジュールのルール

javascriptのルールはモジュールのないでは少し違ってくる。

  • 常にScriptモードで実行されるため、use scriptを追加する必要はない。
  • thisがルートコンテキストで何を示すか
thisがルートコンテキストで何を示すか

ES2015のモジュールではルートレベルのthisはundefinedになる。

var obj = {
  foo() {
    return this;
  }
}

function bar() {
  return this;
}

obj.foo(); //obj
bar(); //undefined
console.log(this) //undefined

モジュールは変数がグローバル変数として定義されることはない。
しかしモジュール内部でグローバルオブジェクトに値を割り当てることは可能。

とはいえモジュールのグローバルの設定は悪い作法。

モジュールを作成

デフォルトエクスポート

グローバル関数はこう書くかもしれない

window.logState = functionn(stat) {
  //処理
}

しかしモジュールではこう書く

export default function logState(start){
  //処理
}

関数の先頭にexportをつけるとこのモジュールがlogState関数をいエクスポートするようになる。このエクスポートのことをデフォルトエクスポートという。

エクスポートしたくない関数があるとき

モジュールを使用せず、ヘルパー関数をエクスポートしたくないとしたらこう書くかもしれない。

{
  function statsHellper() {
    //処理
  }

  window.logState = functionn(stat) {
    //処理
  }

}

しかしモジュールを使うならすべてのものが独自のスコープに含まれているため、値が漏れることはない。

function statsHelper() {
  //処理
}

export default function logState(start) {
  //処理
}

名前付きエクスポート

モジュールを関数として考えるとexport defaultはreturnに相当する。
関数から返せる値は1つであるようにモジュールのデフォルトエクスポートは1つだけ。

しかしデフォルトエクスポートが1つだけなのであって、モジュールのエクスポートが1つに制限されるわけではない。
デフォルト以外は名前付きエクスポートになる。
名前付きは単にdefaltが消えただけ。

export function logState(start) {
  //処理
}

名前は関数名になる。

名前付きエクスポートのもう一つの構文

名前付きエクスポートにはもう一つ構文がある。

function logState(num){
  //処理
}
export{logState}

構文はこう

export{<バインディング1>,<バインディング2>,...}

好きなだけ指定することもできる。

名前を変えてエクスポート

名前を変えてエクスポートもできる
内部でformattedCurrentusernameをusernameでエクスポートするにはこうする。

export { formattedCurrentUsername as username}

変数宣言でも関数同様

変数宣言でも件数と同じことが当てはまる。

export let one = 1//oneという名前でエクスポート
export const two = 2//twoという名前でエクスポート
export 3//無効:名前を推測できない

exportはトップレベルに配置する

条件付きで値をエクスポートはできない。
トップレベルとはファイル内のコードブロックの本体、if文、関数ではなく、コードブロックのルートを意味する。

//トップレベルなので有効
export const number = num => //処理
  //トップレベルでない
if () {
    export const number = num => //処理
}

//トップレベルでない
function
export () {
  export const number = num => //処理
}

javascriptファイルはどのタイミングでモジュールになるか

基本的に<script>のtype='module'とするとそのファイルをモジュールとして実行するようになる。

別の方法

Node.jsには<script>タグを使用しないため上の方法では使えない。
別の方法としてこのようなものがある。

export {}
/* モジュールの残りの部分 */

これはオブジェクトのエクスポートではない。
これは本来なら{}の間にエクスポートする名前を指定する名前付きエクスポート構文。
何もエクスポートされないが、これがあることでこのファイルはモジュールと認識される。

まとめ

  • 宣言をエクスポートするときは、名前付きエクスポートが作成される。
  • 名前付きエクスポートは別名も指定できる。
  • 名前付きはいくつでもいいが、デフォルトエクスポートは1つだけ
  • exportはトップレベルで記述
  • ルートレベルではthisはundefinedになる
  • モジュールはデフォルトでScriptモードになる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DDD特別夏季講習 / 2時限目:ドメインサービス

はじめに

本記事は前回の記事の続きになります。DDDをゼロから学びたい方はまず前回の記事からお読みください。
前回の記事: DDD特別夏季講習 / 1時限目:値オブジェクトとエンティティ

今日は値オブジェクトやエンティティと同じく、ドメインオブジェクトを実装する際に用いられる「ドメインサービス」について解説していきたいいと思います。

対象読者

  • 職場でドメイン駆動開発を使って開発を行っている方
  • これからドメイン駆動開発の導入を検討している方
  • エリック・エバンスの本分厚いよー、ぴえん?って方
  • その他、ドメイン駆動開発ってなにー??って方

ドメインサービス

値オブジェクトとエンティティの表現の限界

まずはじめに説明するのが、ドメインサービスです。
ドメインサービスは、値オブジェクトやエンティティでドメインを表現しようとすると違和感が生じる場合に利用されます。例を挙げて見てみましょう。

class user {
  constructor(name, age){
  this.name = name
  this.age = age
  }

 checkAge() {
  if (this.age < 18) {
  throw new Error("18歳未満の方は入場できません")
 }
 }
}

上記のコードではcheckAgeというメソッドを用意して、ユーザーが18歳未満でないか確認して、もしそうであった場合にはエラーを吐くようになっています。まあ、みなさんがよくご愛用なさっているサービスとかでも、こういった仕様は頻繁に見受けられるかと思います笑

こういったドメインがもつバリデーションは、本来ユースケース層ではなく、上記の例のようにドメイン層で持つべきものです。でないと、ユースケース層でuserを呼び出す度にこのバリデーションの処理を書かなければいけなくなります。こういったドメイン固有のルールの流失は原則として避けるべきものです。

といった感じで、上記のコードは正しいように思われます。ホントに?

では、実際にこのcheckAgeメソッドを呼んでみましょう。

val user = new user("ruirui", 22)

user.checkAge()

ruiruiさんの年齢が18歳未満でないかを確認していますね! 、、、ruiruiさんが。
上記のクラス及びメソッド定義の最大の問題点はここにあります。
これだと、ユーザ自信がユーザの年齢が条件に合致するか否かを判断しているような感じがしてしまいます。クラスに定義されたメソッドというのは、そのオブジェクトの振る舞いを示したものだからです。
ここで今一度、DDDにおける開発の原則に立ち返っていただきたいのですが、DDDとは「現実世界のドメインの知識を、アプリケーションに必要な物にのみ絞り込んだ上で、それをドメインオブジェクトに落とし込む」といった開発手法です。まあ要は、現実世界のドメインを、必要な分だけ取って実装するって感じですね。つまり実装上のドメインの関係性は現実世界に照らし合わされたものである必要があります。
上記の実装は現実の世界のドメインを正確に表しているとは言えませんね。ユーザ自身が、年齢をチェックするわけではありませんからね。

ドメインサービスの強み

じゃあどう実装すればいいんだよ!まあ、勘の良い方ならもうお気づきでしょう。ここでドメインサービスの出番です。

class userDomainService {
  constructor(age) {
  this.age = age
  }
  checkAge() {
  if (this.age < 18) {
  throw new Error("18歳未満の方は入場できません")
 }
 }
}

val user = new user("ruirui", 22)
new userDomainService(user.age).checkAge() // 実際に年齢のバリデーションの処理が走る

これで先ほどよりコードに違和感がなくなった感じがしませんか??
このようにドメインサービスは、値オブジェクトやエンティティだけで現実世界のドメインオブジェクトの関係性を正しく表現した実装が困難な場合に使われます。

上記のケース以外では、口座間送金などがドメインサービスを使う例としてよくあげられます。

ドメインサービスを使う上での注意点

ドメインサービスは諸刃の剣です。
ドメインサービスは、値オブジェクトやエンティティでは表現しきれない部分の表現を補ってくれるとっても便利な存在ですが、その一方で大きな問題もあります。それはドメインから説明を奪ってしまうことです。これをドメインモデル貧血症と言います(めちゃめちゃ語呂がいいですよね笑)。
理論上、ドメインの持つべきあらゆる振る舞いをドメインサービスで実装することも可能です。例えばuserにchangeName()がある、みたいなものですかね。
ドメインサービスにこういったchangeName()などを持たせると、userドメインからその振る舞いが消えていってしまいます。
これだと、コードを見たときにそのuserクラスがいったい何をするものなのか、見えにくくなってしまいます。

ドメインサービスを使うのは、値オブジェクトやエンティティでオブジェクトを表現できないときのみにするのが良さそうです。

終わりに

いかがでしたでしょうか??ドメインサービスを使うと、値オブジェクトやエンティティではうまく表現しきれないものでも、自然な表現でドメインオブジェクトの実装が可能になります。一方、むやみやたらに使ってしまうと、ドメインクラスから知識を奪いすぎてしまい、「現実世界のドメインをそのまま実装に落とし込む」DDDの思想に完全に反する事態を招いてしまうので注意しましょう。

これで今日の授業は終わりです。前回の記事と今回の記事を読んだあなたは、おそらく基本的なドメインを実装に落とし込めるまでにはなったはずです。ですが、DDDの構成要素はドメインを定義するドメイン層と、
それを呼び出して実行する箇所だけではありません。次は主にDB等とのやりとりを行うレポジトリ層について解説したいと思いますのでお楽しみに。

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

SVGを書くのが面倒なので入力補完できるようにしてみる 3

前回

  1. SVGを書くのが面倒なので入力補完できるようにしてみる 1
    内部に持った状態を変更させるオブジェクト、メソッドチェイン、基本シェイプ、<path>のd要素
  2. SVGを書くのが面倒なので入力補完できるようにしてみる 2
    useに潜んでいた罠、<animate>
  3. SVGを書くのが面倒なので入力補完できるようにしてみる 3

引き続き ↓ コレ作ってるときの話です。
GitHub: mafumafuultu/svg.js dev

Render 描画品質

 図形、画像、テキストの描画品質を設定できるんですが、プロパティ名は長いし値も長いのでスペルミスとか怖いし、比較に時間をかけるのも嫌なので、プロパティ名とそれに紐づく値も設定できるようにしたいなー。

↑ こんなこと考えたおかげで新しい仕組を考えることになった。
そして、今回はこのRenderでおなかいっぱいになりそうな予感がする。

条件

  • 図形、画像、テキストのレンダーの設定を一か所にまとめたい
  • レンダーのプロパティ名は長いので補完したい
    • shape-rendering
    • text-rendering
    • image-rendering
  • 各レンダープロパティの値も長い。補完したい
    • optimizeSpeed
    • crispEdges
    • geometricPrecision
    • optimizeLegibility
    • etc.etc...
  • render().shape().optimizeSpeed().text().geometricPrecision() な感じで補完したい
      ↑ 鬼の所業
  • 面倒でも可読性は確保したい
      ↑ 鬼の所業

問題点

まず、この図式

Render => Text => Value-A => Render
Render => Shape => Value-B => Render
Render => Image => Value-C => Render

image.png

 Renderを呼び出し、各レンダープロパティ名を補完、各レンダープロパティは設定可能なValueを補完し、Renderのもとに帰る。

つまりこんな書き方が成立しなければダメだということになる。

render().Text().auto().Text().auto().Text().auto()
    .Shape().auto().Shape().auto()
    .Image().auto().Text().auto().Image().auto().Text().auto();

はてさて、どうしたものか...

デカルト「困難は分割せよ」

 まずは理解できる単位に、構造を分解・整理してやればいい。

Render => [Text  => Value-A] => Render
Render => [Shape => Value-B] => Render
Render => [Image => Value-C] => Render

Render => [property => Value] => Render

T = [property => Value]
Render => T => Render

他人が作ったスパゲッティを読み解くよりは楽です。
見えてくるのはTRenderthisを受け取り、中身を変更して返せばいい。

const __shapeRender = o => ({
    auto() {return o.render['shape-rendering'] = 'auto', o;}
});

const renderGen = () => ({
    render: {'shape-rendering': 'optimizeSpeed'},
    Shape() {return __shapeRender(this);}
});

 まぁ、これで renderGen().Shape().auto().Shape().auto() はできるようになった。
よしよし、イケるイケる。

そんなわけなかった

ダサい。

const __shapeRender = o => ({
    auto() {return o.render['shape-rendering'] = 'auto', o;},
    optimizeSpeed() {return o.render['shape-rendering'] = 'optimizeSpeed', o},
    crispEdges() {return o.render['shape-rendering'] = 'crispEdges', o;},
    geometricPrecision() {return o.render['shape-rendering'] ='geometricPrecision', o;},
});
  • o.render['shape-rendering'] 何度も書いてる
  • shape-rendering に 値を入れてoを返す構造は共通している
  • そして、その構造はほかのrenderにも出てくる

というわけでその辺を解決していく

renderGen().Shape() と呼んだ時点で分かっている情報は

  • shape-rendering に設定される値が後で選ばれる
  • renderGenthis も渡ってくる

なので、こんな仕組みが必要だろう。

const vmod = key => (val, obj) => (obj.render[key] = val, obj);

このままでもいいのだけれども、もう少し疎結合にしておく。

// 役割がわかればいいので、頭文字だけにする
const vmod = (k, f) => (v, o) -> (f(o, k, v), o);
const __renderMap = (o, k, v) => o.render[k] = v;

 任意の (o, k, v) を引数に取る関数を渡せるように変更した。
使うかどうかはわからないけれども、ほかの処理作るときに再利用しやすいようにはなった。
 使用する側は、変更したいプロパティと変更用の関数をvmodに渡して、適当な名前を付けて、閉じ込めておき、どの値を使用するか確定したらその値とrenderGenthisを受け取ってrenderを変更し、renderGenthisを returnする。

const __shapeRender = o => ({
    auto() {return this._set('auto', o)},
    _set: vmod('shape-rendering', __renderMap)
});
const renderGen = () => ({
    render: {'shape-rendering': 'optimizeSpeed'},
    Shape() {return __shapeRender(this);}
});

これで renderGen().Shape().auto().Shape().auto(); ができるようになった。
あとは残りのRenderを仕様とにらめっこしながら書いていくだけなので楽なものです。

面倒な割にはこれで80%というところでしょうか?

 次回は、「あったらいいな」「これ、ほしいな」な便利系の整理です。
使い勝手を決める重要な部分ですね。

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

【Rails】多階層カテゴリーから商品一覧を表示させるプロセスとは

前提条件

以下のことが終わっていることとする。終わっていないと機能が正しくなっているか確認が難しい。
・ancestryを用いてカテゴリーテーブルを作成している。
・カテゴリー:商品 = 1:多の関係になっている。
・カテゴリーの階層が3段階になっておりそれぞれの命名が「親」、「子」、「孫」になっている
・商品モデルのcategory_idには、最下層のカテゴリーidが登録されている。
・gem kaminariがインストール済
・カテゴリーコントローラーファイルの作成及び、カテゴリーの取得ができるコードが書かれている。

不安な人はここをクリック
以下のようなコードが書かれていれば、問題ない。
_Rails_多階層カテゴリーから商品を検索・一覧表示する機能_-_Qiita.png
_Rails_多階層カテゴリーから商品を検索・一覧表示する機能_-_Qiita.png

カテゴリー別に商品一覧を表示させる

カテゴリーの詳細が見たいわけなのでshowアクションを用います。

showアクション定義

app/controllers/categories_controller.rb

before_action :set_category, only: :show

def show
  @items = @category.set_items
  @items = @items.where(buyer_id: nil).order("created_at DESC").page(params[:page]).per(9)
end

private
def set_category
  @category = Category.find(params[:id])
end

モデルメソッド定義

app/models/category.rb

has_many :items
has_ancestry

def set_items
  # 親カテゴリーだった場合
  if self.root?
    start_id = self.indirects.first.id
    end_id = self.indirects.last.id
    items = Item.where(category_id: start_id..end_id)
    return items

    # 子カテゴリーだった場合
  elsif self.has_children?
    start_id = self.children.first.id
    end_id = self.children.last.id
    items = Item.where(category_id: start_id..end_id)
    return items

    # 孫カテゴリーだった場合
  else
    return self.items
  end
end

@items = @category.items と記述するだけでは、
商品モデルのcategory_idには最下層のidが付与されているので@categoryが孫であった場合のみ、情報が取得されることになってしまう。
なので予め、上記のようにカテゴリーが親なのか子なのか孫なのかといったような条件分岐をするとうまくいくはず

ビュー

app/views/categories/show.html.haml

.items-container
  .items-index
    .title
      = "#{@category.name}の商品一覧"
      .title__border
    - if @items
      %ul.lists
        = render "items/item", items: @items
    = paginate @items

このままでは他のカテゴリーへのリンクがまだ設定されていないが、
それはまた、書いていきます

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

Moment.js? Day.js? Javascirpt? Temporal?

Javascript では日付関連の操作をする Date オブジェクトがありましたが API が使いづらいので Moment.js を使うのがメジャーでした。ただ、 Moment.js は翻訳ファイルを含みファイルサイズが大きく、 代替として DAYS.JS などが登場します。また、昨今登場予定の Temporal などがあります。ここら辺の状況をまとめてみたいと思います。

Date オブジェクトは本当に使いにくい?

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date

こちらにまとまっていますが、ざっと見てみると使いにくさはあるものの、巨大なライブラリを使うほどでもない可能性があります。

Date.prototype.getDate()
    // 地方時に基づき、指定された日時の「日」 (1–31) を返します。
Date.prototype.getDay()
    // 地方時に基づき、指定された日時の「曜日」 (0–6) を返します。
Date.prototype.getFullYear()
    // 地方時に基づき、指定された日時の「年」 (4桁の年であれば4桁) を返します。
Date.prototype.getHours()
    // 地方時に基づき、指定された日時の「時」 (0–23) を返します。
Date.prototype.getMilliseconds()
    // 地方時に基づき、指定された日時の「ミリ秒」 (0–999) を返します。
Date.prototype.getMinutes()
    // 地方時に基づき、指定された日時の「分」 (0–59) を返します。
Date.prototype.getMonth()
    // 地方時に基づき、指定された日時の「月」 (0–11) を返します。
Date.prototype.getSeconds()
    // 地方時に基づき、指定された日時の「秒」 (0–59) を返します。

基本的なGetterは備えてます。
注意事項はgetMonth()は0始まりだということはあります。これはJavaからの影響だそうです。

var Xmas95 = new Date('December 25, 1995 23:15:30');
var month = Xmas95.getMonth();

console.log(month); // 11

Setterも備えてます。

Date.prototype.setDate()
    // 地方時に基づき、指定された日時の「日」を設定します。
Date.prototype.setFullYear()
    // 地方時に基づき、指定された日時の「年」を完全な形 (例えば、4桁の年であれば4桁) で設定します。
Date.prototype.setHours()
    // 地方時に基づき、指定された日時の「時」を設定します。
Date.prototype.setMilliseconds()
    // 地方時に基づき、指定された日時の「ミリ秒」を設定します。
Date.prototype.setMinutes()
    // 地方時に基づき、指定された日時の「分」を設定します。
Date.prototype.setMonth()
    // 地方時に基づき、指定された日時の「月」を設定します。
Date.prototype.setSeconds()
    // 地方時に基づき、指定された日時の「秒」を設定します。

Localeも対応しています。

Date.prototype.toLocaleDateString()
    // システム設定の日時の「日付」部を、地域の日付書式に従った文字列に変換して返します。
Date.prototype.toLocaleFormat()
    // 書式化された文字列を使って、日時を文字列に変換します。
Date.prototype.toLocaleString()
    // 日付を地域の日付書式に従った文字列に変換して返します。Object.prototype.toLocaleString() メソッドを上書きします。
Date.prototype.toLocaleTimeString()
    // システム設定の日時の「時刻」部を、地域の日付書式に従った文字列に変換して返します。

意外と必要な操作はできます。特に必要になるまではライブラリは使わなくてもいいもしれません・・・

javascriptとMoment.jsとDAY.JS比較

javascript

new Date().getDate()

Moment.js

moment().date()

DAY.JS

dayjs().date()

Moment.jsとDAY.JSはほぼ変わらないインターフェースを持っています。month()はjavascriptと同じく0始まりなのは注意が必要です。

javascript、そのままとの大きな違いは、add() や weekdays() などの便利なメソッドがあることです。特にadd()などの時間の計算は便利なので時間の計算を使う場合はライブラリを検討したほうがいいかもしれません。

Moment.js

moment().add(7, 'days'); 
// 7日を足す

moment().weekday(-7); // last Monday

DAY.JSの場合はpluginをrequireする必要があります。

dayjs().add(7, 'day')
// 7日を足す

var weekday = require('dayjs/plugin/weekday')
dayjs.extend(weekday)

dayjs().weekday(-7) // last Monday

Moment.jsとDAY.JS比較

weekdays()などのメソッドは Moment.js にしかなさそうです。

moment.weekdays(3)
// "Wednesday"

Timezoneの扱いも異なります。Moment.jsではかなり細かいTimezoneのサポートがありますが、DAYS.JSはpluginでの対応でISSUEは開いてますが、まだFIXはしてません。
https://momentjs.com/timezone/docs/
https://github.com/iamkun/dayjs/issues/323

これらの便利なメソッドやTimezoneのサポートが必要であれば Moment.js を検討しても良さそうです。

Temporal とは?

Temporal とは ECMAScript を策定してる TC39 が新しく仕様を提案して、いまフィードバックを募集している仕様のことで、これが採用されれば標準ライブラリとして使えるようになります。
https://blogs.igalia.com/compilers/2020/06/23/dates-and-times-in-javascript/

実際のユースケースに適しているかのフィードバックを募集しているので、もし興味があれば是非フィードバックを送ってみてください。

npm install --save proposal-temporal

をした後requireすることで使えます。

const { Temporal } = require('proposal-temporal');

API のドキュメントは下記です。
https://tc39.es/proposal-temporal/docs/

既存のライブラリとの違いとしては、Dealing only with immutable objectsとあり、immutableであり、あとから時間情報の変更ができないことが、まず特筆されます。これは非常に便利そうです。

ConsoleにてTempralを呼び出すと下記のような感じです。

スクリーンショット 2020-07-14 22.07.20.png

TimeZoneとDateTimeとDateとTimeがそれぞれ分かれているのは便利そうな感じがします。

// IANA time zone names and UTC offsets
tz = Temporal.TimeZone.from('UTC');
tz = Temporal.TimeZone.from('Africa/Cairo');
tz = Temporal.TimeZone.from('america/VANCOUVER');
tz = Temporal.TimeZone.from('Asia/Katmandu');  // alias of Asia/Kathmandu
tz = Temporal.TimeZone.from('-04:00');
tz = Temporal.TimeZone.from('+0645');


// Converting a specific absolute time to a calendar date / wall-clock time
timestamp = new Temporal.Absolute(1553993100000000000n);
tz = new Temporal.TimeZone('Europe/Berlin');
tz.getDateTimeFor(timestamp);  // => 2019-03-31T01:45
tz.getDateTimeFor(timestamp).day;  // => 31

こんな感じでTimeZoneを扱えます。

date = Temporal.Date.from('2006-08-24');
date.plus({years: 20, months: 4})  // => 2026-12-24

日付の計算はこのような感じです。
こうした仕様が確定してくると、ひょっとすると Moment.js や DAY.JS を使わない日が来るかもしれません。
(似たようなTempralのWrapperライブらいが出るかもしれません)

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

【javascript】ジェネレータ関数

書籍のアウトプットとして

ジェネレータ関数

//ジェネレータ関数にはアスタリスクが付いている。
function *myGeneratorFunction(){
  //...
}

//ジェネレータ関数の呼び出しではnewを使用しない
const myGenerator=myGeneratorFunction()
const myObj={
  *myGen(){//オブジェクトリテラルでの簡潔なジェネレータメソッド
    //..
  }
}

イールディング

ジェネレータ関数内のコードは通常の関数と同じように動作するが、1つ大きな注意点がある。
javascriptでは関数の内側で飲み使えるyieldが追加されている。
これにより関数内側と外側との間に双方向の通信チャンネルが作成される。

関数の実行はyieldが検出されたとことで中断、その関数を呼出した外側のコードに制御が戻される。
yieldを使用するときは、returnと同様に外側のプロセスに値を渡すこともできる。

function* myGeneratorFunction(){
  //...
  const message='Hello';
  yield message;//実行はここで中断し、外側のプロセスに'Hello'が返される
}

yieldは実際には式であり、関数内であとから使用する値を取得できる。

function* myGeneratorFunction(){
  //...
  const message='Hello';
  yield message;//外側からのプロセスからの値を補足
  //...
}

ジェネレータ関数の使用

ジェネレータ関数を呼び出すと、新しいジェネレータオブジェクトが返される。このジェネレータオブジェクは最初は停止状態で、nextメソッドを呼び出すまで何もしない。

function* myGeneratorFunction() {
  connsole.log('This code is now running')
}

myGeneratorFunction() //この時点ではlogは評価されない

const myGenerator = myGeneratorFunction(); //ここでも評価されない。

myGenerator.next(); //ようやく評価される。

yieldがある場合は呼び出しまで何もしない

function* myGeneratorFunction() {
  console.log('A')
  yield;
  console.log('B')
}
const myGenerator = myGeneratorFunction();

myGenerator.next(); //A
myGenerator.next(); //B

nextメソッドを呼び出すたびにvalueとdoneの2つのプロパティを持つオブジェクトが返される。

プロパティ 説明
value yieldに渡された値を含んでいる。
done ジェネレータが中断状態7日終了したのかを示す。
function* myGeneratorFunction() {
  console.log('Started')
  let recievedA = yield 'a'
  console.log('recievedA:', recievedA)
  let recievedB = yield 'b'
  console.log('recievedB:', recievedB)
}
const myGenerator = myGeneratorFunction();
let gotA = myGenerator.next(0); //Started
console.log('gotA', gotA) //gotA {value: "a", done: false}
let gotB = myGenerator.next(1); //recievedA: 1
console.log('gotB', gotB) //gotB {value: "b", done: false}
let gotC = myGenerator.next(2); //recievedB: 2
console.log('gotC', gotC) //gotC {value: undefined, done: true}

ジェネレータ関数から最後に返されたオブジェクトのdoneプロパティにはtrueが設定されて、valueがundefinedになっている。

また、最初のnext呼び出しに渡された0が使用されない。これは最初のnextが以降のようにyieldに関連付けられていないため。

最初のnext呼び出しはジェネレータを初期状態から呼び起こすためのもの。

まとめ

  • ジェネレータ関数はアスタリスクで定義
  • ジェレネータ関数はジェネレータオブジェクトを返す
  • ジェレネータはyieldで値を返せる
  • nextで次の処理を実行できる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptを使って簡単な計算機を作る 入門者向け

計算機を作る

きっかけ

Udemyで学んだばかりの知識をアウトプットするため

Udemy

Udemy
ウェブ開発入門完全攻略コース - プログラミング をはじめて学び創れる人へ!未経験から現場で使える開発スキルを習得!
https://www.udemy.com/course/web-application-development/

主にセクション9(Javascript入門)及びセクション10(Javascript DOM操作)の知識

ざっくりとした構想

テキスト入力計算機(今回は足し算のみ)を作る
2つの数値入力欄を作成し、入力された値を取得
取得したのちに、足し算を行う処理を書き
結果をli要素としてul要素に追加していく

完成物(スクリーンショット)

スクリーンショット 2020-07-14 21.46.24.png

二つのテキストボックスに数値を入力し、計算ボタンを押すと足し算が行われ、上のスペースに計算結果を表示している。

サンプルコード

addtion.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>jsを使って足し算</title>
  </head>
  <body>
    <ul id="lists"></ul>
    <input id="number1" type="text">
    <span>+</span>
    <input id="number2" type="text">
    <input id="calculate" type="button" value="計算">

    <script src="js/addtion.js"></script>
  </body>
</html>
addtion.js
document.getElementById('calculate').onclick = function () {
  var li = document.createElement('li');
  var number1 = parseFloat(document.getElementById('number1').value);
  var number2 = parseFloat(document.getElementById('number2').value);

  var result = document.getElementById('calculate').textContent = number1 + number2;
  var number = document.createTextNode(result);

  li.appendChild(number);

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

参考サイト

<input type="text">で数値のみ入力する方法

Javascriptの積の計算結果が出て来ません

Javascript MDN:parseFloat()

今後の構想

0〜9までの数字ボタンで数値を入力する機能と四則計算が行える計算機を作る
一気に作ると大変なので、次回は引き算機能追加など、少しずつ機能を追加していく。

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

簡単レシート印刷 receiptline で文字装飾してみた

日本発のオープンソース receiptline でレシート印刷に少しずつトライしています。
まだレシートプリンターがないので、前回セットアップした開発ツールを引き続き使います。
今回は文字装飾です。

01.png

文字装飾の種類

文字装飾は 4 種類。
アイコンは左から、アンダーライン、強調、白黒反転、拡大です。
クリックすると文字装飾記号を挿入します。

02.png

キーボードで文字装飾記号を直接入力しても OK。
_ はアンダーライン、" は強調、` は白黒反転、^ は拡大です。

装飾の開始

文字装飾記号を挿入すると、それ以降の文字が装飾されます。

_hello, world!
"hello, world!
`hello, world!
^hello, world!

03.png

装飾の範囲

同じ文字装飾記号で囲むと、その範囲にある文字が装飾されます。

_hello_, world!
"hello", world!
`hello`, world!
^hello^, world!

04.png

装飾の組み合わせ

文字装飾を組み合わせることができます。
範囲を入れ子にしなくても大丈夫。

"`hello", world!`

05.png

拡大いろいろ

拡大文字装飾記号を重ねると、さらに大きな文字になります。

hello
^hello
^^hello
^^^hello
^^^^hello
^^^^^hello
^^^^^^hello
^^^^^^^hello

06.png

装飾記号の出力

文字装飾記号の前に \ を付けると、普通の文字になります。

\"hello, world!\"

07.png

水平線と用紙カット

普通の文字装飾とは少し違うものもあります。
アイコンは左が水平線、右が用紙カットです。クリックすると記号を挿入します。

08.png

- は水平線、= は用紙カットです。キーボードからの直接入力もできます。
これらの特殊記号は、1 行に他の文字と混在しないときに有効です。

hello, world!
---
hello, world!
===
hello, world!

09.png

次回は位置揃えを試してみようと思います。

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

Kinx ライブラリ - JIT ライブラリ(番外編)

Kinx ライブラリ - JIT ライブラリ(番外編)

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。今回は JIT ライブラリ番外編です。

ここ で紹介した JIT ライブラリに新規追加した機能です。

JIT ライブラリ に任意のバイナリコードを実行できる機能を追加してみました。本気で細かく制御したい人向けです。こちらはがっつりアーキテクチャ依存です。そして、クラッシュさせるのも簡単です。

サンプル

まずはサンプルです。

okay.kx
using Jit;

var code
    = System.PLATFORM == "X86_64-WIN" ? <0x48, 0x89, 0xc8, 0xc3>    // mov rax, rcx | ret
    : System.PLATFORM == "X86_64"     ? <0x48, 0x89, 0xf8, 0xc3>    // mov rax, rdi | ret
    : null;
if (code.isBinary) {
    Jit.dump(code);
    var runner = new Jit.Runner(code);
    System.println(runner.run(100));
}
$ ./kinx okay.kx
       0:   48 89 f8                                    mov rax, rdi
       3:   c3                                          ret
100

さて、引数で与えた数値を単に返すだけの関数で、破壊するレジスタも無いのでプロローグもエピローグもなく、第一引数に来た値を復帰値(rax)に設定して ret するだけのものです。

System.PLATFORM

System.PLATFORM で x64 でかつ Windows か Windows ではないかを切り分けてます。生のアセンブラだとこうやって切り分けないといけないのが面倒ですね。しかし、何でもできる という危険で甘い香りのするメリットを存分に享受できます。

Windows だと Microsoft 呼び出し規約に基づくので第一引数は rcx レジスタに入ってきます。それに対し、ほぼほぼマイクロソフト以外が採用している System V 呼び出し規約では rdi レジスタに第一引数が入ってきます。

System.PLATFORM で具体的には何が返るかというと...

Value Window?
"X86_32-WIN" O
"X86_64-WIN" O
"ARM_THUMB2-WIN" O
"ARM_V7-WIN" O
"ARM_V5-WIN" O
"ARM_64-WIN" O
"X86_32"
"X86_64"
"ARM_THUMB2"
"ARM_V7"
"ARM_V5"
"ARM_64"
"PPC_64"
"PPC_32"
"MIPS_32"
"MIPS_64"
"SPARC_32"
"TILEGX"
"UNSUPPORTED"

です。

やってはいけないこと

crash.kx
using Jit;
var code = <0x48, 0x31, 0xc0,   // xor rax, rax
            0x48, 0x8b, 0x00>   // mov rax, [rax]
            ;
Jit.dump(code);
var runner = new Jit.Runner(code);
System.println(runner.run());
$ ./kinx crash.kx
       0:   48 31 c0                                    xor rax, rax
       3:   48 8b 00                                    mov rax, [rax]
Segmentation fault (core dumped)

ふふふ...。危ないなぁ。

おわりに

一言でいうと、"Take your own risk." といったところですね。しかし、こういうところに踏み込むスクリプト言語はなかなか無いと思うので、それなりの価値はあるのではないでしょうか。例えば、このライブラリがあれば Xbyak なんかも移植できそうですね! Kinx には演算子オーバーライド(オーバーロードとは呼ばない)もありますし。

ますます JIT が身近になりますね。

ではまた。

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

【javascript】アロー関数

書籍のアウトプットとして

アロー関数を使ってコードを簡潔にする

アローの構文は2つに分かれる。

パラメータと関数本体の式が1つずつの場合

<単一のパラメータ>=><return 式>

パラメータが1つではない場合(ないか、2つ以上の場合)

()をつける

const double = x => x + x;
const add = (a, b) => a + b;
const rand = () => Math.random();

単一パラメータを()で囲んでもよい

レストパラメータと分割されたパラメータボぼの場合は()で囲む

const rest = (...args) => console.log(args) //正しい構文
const rest = ...args => console.log(args) //構文エラー
const destruct = ([first]) => first //正しい構文
const destruct = [first] => first //構文エラー

関数本体に四季が2つ以上含まれる場合は関数本体を波括弧で囲む。

const d=(a,b)=>{
  task();
  list();
}

アロー関数が簡潔になるケース

関数から別の関数を返さなければいけないとき。
下の関数は数字を1つ渡すと別の関数が返され、返された関数は指定された奇数にべき乗を適用する。

const exponent = exp => base => base ** exp;

const square = exponent(2)
const cube = exponent(3)
const power0f4 = exponent(4)

console.log(square(5))//25
console.log(cube(5))//235
console.log(power0f4(5))//635

**は1つ目のオペランド2つ目のオペランドでべき乗する。

これのES5の場合

var exponent = function(exp) {
  return function(base) {
    return Math, pow(nase, exp)
  }
}

次の総和関数も書き換えることができる。

const sum = function(...argd) {
  return args.reduce(function(a, b) {
    return a + b;
  });
}

こうなる

const sum = (...args) => args.reduce((a, b) => a + b)

reduce

構文
arr.reduce(callback( accumulator, currentValue[, index[, array]] )[, initialValue])

callback

配列のすべての要素に実行される。4つの引数を取る。

引数番号 引数 説明
第1 accumulator コールバックの前回呼び出しで返された値
第2 currentValue 現在処理中の要素
第3 index(optional) 現在処理中のインデックス
第4 array(optional) reduce()が呼び出された配列

initialValue

コールバックの最初の呼び出しの最初の引数として使用する値。

返り値

1つ値

アロー関数の注意点

アロー関数は常に関数式であり、関数定義ではない。

// 関数定義
function double(number) {
  return number * 2
}
//関数式
const double = functin(number) {
  return number * 2
}

//アロー関数(関数式に相当)
const double = number => number * 2;

つまり、アロー関数を関数定義のように巻き上げることはできない

また、アロー関数からオブジェクトリテラルを暗黙的に返そうとした場合困る。

const getSize=()=>{weight:50,height:50}//syntaxError

アロー関数の=>に続いて{}が存在する場合、それらの波括弧は常に関数本体の開始と終了であるとみなされる。
これは()で囲めばいい

const getSize=()=>({weight:50,height:50})//動く

最後にアロー関数のコンテキストにbind,call,applyは使えない

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

Top 3 công nghệ lập trình giúp sinh viên IT

business-infographic-with-photo_23-2148340471.jpg

JavaScriptテクノロジー

JavaScriptは、1995年に作成された唯一のクライアント側スクリプト言語です。JavaScriptは非常に人気があり、インターネット上のほとんど
のWebサイトで使用されている非常に高いレベルのアプリケーションを備えています。高い双方向性を作成します。

Python、Javaと同様に、JavaScriptはWebプログラミング、モバイルアプリケーション、デスクトップアプリケーション、ゲームに使用されます。スライドショーに加えて、ポップアップ広告とGoogleのオートコンプリート機能もJavaScriptプログラミング言語で書かれた「優れた」ものです。

ビッグデータテクノロジー

ビッグデータは、従来のアプリケーションとツールの容量を超えるデータの集まりです。このテクノロジーは2001年から形成され、多くの重要なアプリケーションがあります。

企業の場合:ユーザーの行動データの収集に役立ち、企業は利益を増やすためにニーズに合わせたビジネスソリューションを提供できます。 EBay、Amazonのショッピングアプリケーションは、ビッグデータの製品です。 Software AG、Oracle、IBM、Microsoft、SAP、EMC、HP、Dellは、このテクノロジーのアプリケーションのリーダーです。

非政府組織の場合:ビッグデータは、データ分析活動、失業率の予測、将来のキャリアトレンドを大きくサポートします...

一方、このビッグデータソースは、人々がより良い買い物と消費体験をするのにも役立ちます。
Javaテクノロジー

Javaは、最も完全なプラットフォームとオブジェクト指向の独立性を持つ人気のあるプログラミング言語でもあります。この言語は1990年代に形成および開発されましたが、アプリケーションは密接で重要なため、これまでのところ「ホット」を減らしていません。

この言語は、電子商取引や科学的なウェブサイトの作成から、電子金融取引の分野でのアプリケーションまで、かなり多く使用されています。ゴールドマンサックス、シティグループ、バークレイズ、スタンダードチャータードなどのグローバル投資銀行は、Javaを使用して電子取引システム、検証および監査システム、データ処理プロジェクト、および多数のその他の重要な作業。 Java上のアプリケーションに加えて、ゲームアプリケーション、デスクトップアプリケーションなどを作成するための重要なコンポーネントとしても知られています。
おわりに
上記のテクノロジーの一部を使用して、教育、統計、販売、SEOなどの実際に適用します...
それらを使用して大きな成功を収めているWebサイトを参照してください。
地点:
=> https://soicau6868.net/soi-cau-888/
=> https://soicau6868.net/soi-cau-rong-bach-kim/
=> https://soicau6868.net/soi-cau-247/
読んでくれてありがとう

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

ESLint 7.4.0

v7.3.0 | 次 (2020-07-18 JST)

ESLint 7.4.0 がリリースされました。小さな機能追加とバグ修正が含まれています。

  • Optional Chaining 構文対応はこちらで進行中です。もうしばらくお待ちください。
  • 前回発生していたリグレッションは Revert されました。

質問やバグ報告等ありましたら、お気軽にこちらまでお寄せください。

? 日本語 Issue 管理リポジトリ
? 日本語サポート チャット (招待リンク)
? 本家リポジトリ
? 本家サポート チャット (招待リンク)


[PR] ESLint は開発リソースを確保するための寄付を募っています。
応援してくださると嬉しいです。


✨ 本体への機能追加

特になし

? 新しいルール

id-denylist

? #13408

既存の id-blacklistid-denylist に名称変更されました。
id-blacklist も、id-denylist の別名として、引き続き利用できます。

? オプションが追加されたルール

camelcase ignoreGlobals

? #12782

グローバル変数を無視するオプションが追加されました。

/*eslint camelcase: [error, { ignoreGlobals: true }] */
/*globals fizz_buzz */

//✔ GOOD
fizz_buzz = 0

//✖ BAD
const foo_bar = 0

Open Online Demo

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

【Nuxt.js】Nuxt文法編:mixin

mixinとは

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

再利用可能な部品たちを
まとめて書いて使いまわせるもの✨
変更したい時はmixinだけ変更すればOK?‍♀️
管理がとっても便利になります!

実際に例を見ていきましょう?

簡単な使い方( js )

jsファイルを使いまわしてみます?

グローバルで使用することはないので
ローカルでご紹介??

ローカルで使用

スクリーンショット 2020-07-13 14.30.12.png

ただconsoleを出すだけのシンプルなもの

file
mixins/
--| mixin.js
pages/
--| index.vue

【mixin.js】
mixinsフォルダを作成し
その中にmixin.jsを作成

mixin.js
export default {
 created () {
   this.hello()
 },
 methods: {
   hello () {
     console.log('hello from mixin!')
   }
 },
}

【index.vue】
mixinをimportするだけ

index.vue
<template>
 <div class="page">
 </div>
</template>

<script>
import Mixin from '../mixin/mixin.js'

export default {
 mixins: [ Mixin ],
}
</script>

コンフリクトが起きた場合はマージされる

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

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

【javascript】パラメータの分割

配列パラメータを分割する

2つのファイルの差分を取るプログラムを作成するとする。
diff関数は2つの引数に対して以下の3つの値を返す。

  • 追加されたテキスト
  • 削除されたテキスト
  • 変更されたテキスト
function visualize(inserted,deleted,modified){
  /*可視化した差分を画面に表示*/
}

const [inserted,deleted,modified]=diff(fileA,gileB);
visualize(inserted,deleted,modified);

もっとスキリさせた方法はこう

visualize(diff(fileA,gileB));

よってvisualize関数を再構築する。

function visualize(diff){
  const inserted=diff[0];
  const deleted=diff[1];
  const modified=diff[2];
  /*可視化した差分を画面に表示*/
}

配列の分割を使えばもっときれいになる。

function visualize(diff){
 const [inserted,deleted,modified]=diff
  /*可視化した差分を画面に表示*/
}

パラメータリストで分割できればもっと無駄がなくなる。

function visualize([inserted,deleted,modified]){
  /*可視化した差分を画面に表示*/
}

オブジェクトパラメータを分割する

diff関数を更新してinserted,deleted,modifiedの3つのプロパティを持つオブジェクトを返すようにした場合はどうなるか。
また、この新しいフォーマットからデータを抽出するようにvisualize関数を更新するとどうなるか。

function visualize({inserted,deleted,modified}){
  /*可視化した差分を画面に表示*/
}
visualize(diff(fileA,fileB))

パラメータリストの角カッコ([])を波括弧にする。
つまり、配列の値をそれらの位置に基づいて取り出す代わりにオブジェクトのプロパティを実際のプロパティに基づいて取り出している。
ここで整理してみる。

分割 説明
配列の分割 要素の位置が正しい限り好きな名前を使用できる。
オブジェクトの分割 プロパティの名前が完全に一致している限りプロパティを好きな順序で取り出せる。

通常の配列とオブジェクトの分割がパラメータリストで実行されるだけ。

名前付きパラメータをシミュレート

function car(make,model,year){
  /*自動車を組み立てる*/
}

このパラメータをオプションにしてデフォルト値を設定したい。
3つめのパラメータにだけ設定する場合はどうするか。
パラメータリストでオブジェクトを分割すれば名前付きパラメータをシミュレートできる。

つまり位置ではなく名前で設定できる。

function car({make,model,year}){
  /*自動車を組み立てる*/
}
let classic=car({year:1965})

しかし、これには問題がある。
このままyearだけを渡すと他のキーが書けたままになってしまう。
もっといい方法はこう

function car({make='Ford',model='Mustang',year=2017}){
  /*自動車を組み立てる*/
}
//yearが設定されほかはデフォルト値が設定される。
let classic=car({year:1965})

オブジェクトを分割して各キーにデフォルト値を設定する。
これによりyearだけを渡しても他のパラメータはデフォルト値が入る。

後は引数を設定せずにcar関数を呼び出す。

function car({make='Ford',model='Mustang',year=2017}){
  /*自動車を組み立てる*/
}
//不明なオブジェクトを分割市要素するためここでエラー
let classic=car()

この場合はcar({})のように呼び出す必要がある。
オブジェクトを渡さない場合、引数はundefinedになるため。

解決するためには各キーのデフォルト値として特定の値を指定するのではなく、パラメータ全体のデフォルト値としてからのオブジェクトを指定する。

function car({make='Ford',model='Mustang',year=2017}={}){
  /*自動車を組み立てる*/
}
//すべてデフォルト値が設定される。
let classic=car()
//year以外はデフォルト値が設定される。
let classic=car({year:1965})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

if文でfalse判定される条件式(JS)

if (undefined) {
  // 実行されない
}

if (null) {
  // 実行されない
}

if (0) {
  // 実行されない
}

if ('') {
  // 実行されない
}

if (false) {
  // 実行されない
}
  • undefined
  • null
  • 0
  • 空文字
  • false

は全てif文内ではfalse判定されます。(雰囲気で使っていたのでまとめた←)
上記項目の頭に!をつけて使うと便利に使えます。
(例) if (!null) {}

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

【Vue】Vue3でScopedCSSが改善されるという話

更新内容

https://github.com/vuejs/rfcs/pull/119

ScopedCSSの改善

以下のissueで議論されている下記の問題がこの更新で改善されます。
https://github.com/vuejs/vue/issues/11245#issuecomment-604150460

scoped によって、親コンポーネントのスタイルは子コンポーネントに漏れません。ただし、子コンポーネントのルートノードは親スコープの CSS と子スコープの CSS と両方によって影響を受けます。これは、設計上、親はレイアウトが目的で子のルート要素をスタイルすることができます。
https://vue-loader-v14.vuejs.org/ja/features/scoped-css.html

v-slot, ::v-deep, scopedのスタイリング記述方法の改善

ScopedCSSの記述方法が改善されます。
ディープセレクタの記述方法は従来,以下の記述でしたが,

style2.vue
<style>
.a ::v-deep b ...
.a >>> b ...
.a /deep/ b ...
</style>
or
<style scoped>
.a ::v-deep b ...
.a >>> b ...
.a /deep/ b ...
</style>

以下のような記述ができて、v-slot, ::v-deep, scopedの記述がより直感的にスタイリングできるようになるみたいです。

style3.vue
<style> <!-- global -->
/* deep selectors */
.a ::v-deep(.b) ...

/* slot */
.a ::v-slotted(.b) ...

/* global */
.a ::v-global(.b) ...
</style>

<style scoped> <!-- scoped -->
</style>

<style deep> <!-- ::v-deep -->
</style>

<style slotted> <!-- ::v-slotted -->
</style>

おわりに

何か間違えや質問などあればコメントお願いします。

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

GASで第一営業日が土日だったら平日に実行させる、をしようとしたら汎用性ありそうなコードに

会社で総務的な仕事をしていると、
第一営業日に必ずこんなことをやる!という業務が出てきますよね。

今回は、GoogleAppsScriptのトリガーで「毎月1日」に実行する設定をしているけど、1日が平日じゃなければ月曜に実行させたい

という気持ちで書いたコードを記載します。

外部の会社と複数のチャットワークチャンネルでやり取りしていて、毎月1日に全チャンネルに「請求書ください」とつぶやくbotを作成したくて以下のようなコードを書きました

function checkDay(){
  var day = new Date();
  var setTime = new Date();
  var day_num = day.getDay();

  //曜日毎の処理わけ
  if(day_num != 0 || day_num != 6){
    sendCw()

  } else if (day_num == 0 ){
    setTime.setDate(setTime.getDate() + 1);
    setTime.setHours(9);
    setTime.setMinutes(00);
    ScriptApp.newTrigger('sendCw').timeBased().at(setTime).create();

  } else if (day_num == 6){
    setTime.setDate(setTime.getDate() + 2);
    setTime.setHours(9);
    setTime.setMinutes(00);
    ScriptApp.newTrigger('sendCw').timeBased().at(setTime).create();
  }
}

function sendCw(){
 //チャットワークに通知する設定
}

これだけです。

詳細

var day_num = day.getDay();
これでまず、曜日を取得します。
曜日は数字としてデータが返ってきます。
それぞれ、以下のように対応しています。
0 = 日曜
1 = 月曜
2 = 火曜
3 = 水曜
4 = 木曜
5 = 金曜
6 = 土曜

なので、実行した日が
0(日曜)もしくは6(土曜)以外。
つまり平日であれば、その日のうちにSendCWを行います。

もし、実行した日が日曜や土曜なら、次の処理に進みます。
次が私的に少し詰まったところです。

利用したのは、
ScriptApp.newTrigger
というものです。

これは、読んで字の如しですが
新しくトリガーをセットできるものです。
今回は、
土曜であれば今日+2日後(月曜)
日曜であれば今日+1日後(月曜)
の、それぞれ9時に、sendCwというファンクションを動かす
という設定を行いました。

この、日時の入れ方はいろいろあるようで、かなり混乱しましたが、こちらの記事を参照し、解決まで進みました。

トリガーを新たにセットできるとなると、かなりできることも広がりますね!

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

styled-componentsを使ってCSS Animationを実装する方法

はじめに

styled-componentsはReactコンポーネント内で使えるCSS in JSの一種です。
スコープをコンポーネントのローカルに保てる点や、Sass記法で記述できる点などが特徴です。

通常のCSSであれば@keyframesを使ってアニメーションを記述できますが、styled-componentsではどうでしょうか。

styled-componentsでのkeyframes

結論から言うと、ほぼpure CSSと同様の記述でkeyframesを実装できます。
(keyframesのimport文が必要になります)

animation.js
import styled, { keyframes } from 'styled-components';

const fadeIn = keyframes`
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
`;
const FadeIn = styled.div`
  animation: ${fadeIn} .5s ease-in-out;
`;

// JSXで<FadeIn>コンポーネントを使うとkeyframes: fadeInが適用される

参考

Basics - styled-components

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

Vue.jsの環境構築まとめ

Vue.jsというJavaScriptフレームワークを勉強しようと環境構築にちょっと時間がかかってしまったのでフロートしてのこしておきます[Windows]

Vue.jsにちょっと触れてみる

簡単なVue.jsを作成して雰囲気をつかんでみる

CDNで読み込むだけなので簡単に確認できる

<script src="https://unpkg.com/vue"></script>

</head>タグのすぐ上に挿入する

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue.jsサンプル</title>
  <script src="https://unpkg.com/vue"></script>
</head>
<body>
  <h1>Vue.js</h1>
  <div id="app">
    {{ message }}
  </div>

  <script>
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue'
      }
    });
  </script>
</body>
</html>

Hello表示を確認するのに一般的なコードですね

{{ message }}の部分が Hello Vue になって表示されます

なるほどつかいやすそうな気がする

ということで導入していきます

プログラミングの世界には便利なものはまとめて管理しようとする流れがあります

【Vue.js】も【npm】とうパッケージ管理ツールで利用できます

【npm】は【Node.js】というプログラグに組み込まれています

  • 【Node.js】の中の【npm】をつかって【Vue.js】を管理する流れです

まずはNode.jsをダウンロード、インストールしましょう

Node.jsのダウンロード

インストールしたらコマンドプロンプトから【Vue CLI】とインストールしましょう

ディレクトリを移動して下記を入力

npm install -g @vue/cli

ちょっと時間かかります

次に【cli-service-global】ファイルも必要になるのでインストール

npm install -g @vue/cli-service-global

これでVue CLIを使用できます
Vue.jsのコマンドツールなので後々役立つので入れていきましょう

まとめ

構築は比較的簡単な方だったとおもいますがディレクトリの移動や、コマンドを忘れてしまったりと細かいところでつまづきやすいです

メモとして残っていると安心です

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

連想配列をsortする

sort関数について

  • 単純なsort

文字コード順に並び替えられる。

const suji = [50, 10, 100];
console.log(suji.sort()); // [ 10, 100, 50 ]
  • 数値順に並び替えsort

数値順に並び変えるのなら、以下のように比較関数にします。これで、数値の大きさ順に配列が並び替える

const suji = [50, 10, 100];
console.log(suji.sort(function(x, y) {
    return x - y;
})); //  [ 10, 50, 100 ]

ここでしていることは、最初は50 - 10をして正数が返るから50は10より後で、50 - 100では負数が返るから50は100より前に来るというようにしているだけです。それを配列内の値で、順々に関数のxとyの引数に渡して位置を並び替えています。
以下のURLが非常に分かりやすい。
参考:https://furukawahiroaki.com/javascript-sort.html

連想配列のsort

ポイントは連想配列のkeyから配列のindexを取得できる findIndexメソッドを使用すること。
以下はarrのnameの順番に基づいて、objの配列を変更している。

let arr = [
{name:'hoge',num:9},
{name:'huga',num:11},
{name:'test',num:13},
]

let obj = [
{name:'huga',num:10},
{name:'hoge',num:5},
{name:'test',num:11}
]


obj.sort(function(x,y){
   return arr.findIndex(x.name) - arr.findIndex(y.name)
})

console.log(obj)

//出力
// obj = [
// {name:'hoge',num:5},
// {name:'huga',num:10},
// {name:'test',num:11}
// ]

参考:https://qiita.com/Test_test/items/7d532f445f2980e896d0

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

マウスのみで今Qiitaの最新記事を猫に喋らせたいなら

Image from Gyazo

猫といっても見た目憎たらし…(おっと誰か来たようだ…)

はじめに

Scratch3.0で拡張ブロックを作ってみたくてWebAPIを利用するブロックを作ってみました!

また、この記事はQiita夏祭り2020_パソナテックのエントリー記事です。
テーマページはこちら

言語としてはJavaScriptで拡張ブロックを追加していきます!

Scratchで拡張ブロックを作る

参考記事
* https://qiita.com/Hiroyuki_OSAKI/items/a46e1c881d3aed4661f7
* http://www.moonmile.net/blog/archives/10331

たいへん参考になりました。
やはりたくさん情報がありますね。
しかし、vmとguiのフォルダ行き来するのは確かに大変…

ソースコード

今回はaxiosを使ってQiitaのAPIを利用します。
なのでまずaxiosのインストールです。

npm i axios

この後、ブラウザリロードだけだとだめで、一度ローカルで立ち上げているScratchを落とし

yarn start

でもう一度立ち上げが必要でした。

以下、scratch-vm/src/extensions/scratch3_newblocks/index.js内の一部です。

const axios = require("axios");
()
         {
                    opcode: "tagBody",
                    blockType: BlockType.REPORTER,
                    text: "tag: [TEXT] の最新記事の内容",
                    arguments: {
                        TEXT: {
                            type: ArgumentType.STRING,
                            defaultValue: "Qiita",
                        },
                    },
                },
()
    tagBody(args) {
        const tag = Cast.toString(args.TEXT);
        return axios
            .get("https://qiita.com/api/v2/items?per_page=1&query=tag:" + tag)
            .then((response) => {
                console.log(response.data);
                return response.data[0].body;
            })
            .catch((err) => {
                console.log("err:", err);
                return err;
            });
()

略が多いですね。触るファイルも多ければ飛び飛びで書かなきゃですし…

使ってみる

試しにスクラッチキャットに喋らせてみました。

Image from Gyazo

インテリな猫になりましたね…
かわいィィぃぃeeee!!

おわりに

拡張ブロックを作るのは一回わかれば簡単にできそうですが、やはり色々なファイルや箇所も飛び飛びな場所を触らなきゃだったりなので怖いですね。

でもブロック作るのは楽しい!

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

Next.jsで"document is not defined." "window is not defined."のエラーが出たとき

Next.jsで"document is not defined." "window is not defined."のエラーが出たとき

原因

Next.jsのSSRが原因である可能性が高いです。

SSR = Server Side Rendering

本来クライアント(ブラウザ)側でしか動かないJSフレームワークなどによるレンダリングをサーバー側で行うことによって、初期表示時の高速読み込みや検索エンジン最適化(SEO)を向上するもの。

サーバー側でJSの処理を実行しようとしているときに、ブラウザ側にしか存在しないグローバルオブジェクトのwindowdocumentを参照しようとすると、標題のエラーが起こります。

対策

1. componentDidMount()内で処理を行う

componentDidMount()内に書いた処理は、基本的にcomponentがmountされた後にクライアント側のみで起こる処理なので、このエラーは起こらないようです。

2. if文で条件分岐

React Hooksが主流の現在、こちらの方が汎用性が高いです。

ssr1.js
if (process.browser) {
  // windowやdocumentを使う処理を記述
}

もしくはシンプルに:

ssr2.js
if (window) {
  // windowを使う処理を記述
}

なおundefinedを判定基準として使うことは一般的にアンチパターンとされるため、下記は避けたほうがいいと思われます。

ssr3.js
if (typeof window !== "undefined") {
  // windowを使う処理を記述
}

参考

Stack Overflow - Window is not defined in Next.js React app

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

Next.jsで"document is not defined." "window is not defined."のエラーが出たときの対処法

Next.jsで"document is not defined." "window is not defined."のエラーが出たときの対処法

原因

Next.jsのSSRが原因である可能性が高いです。

SSR = Server Side Rendering

本来クライアント(ブラウザ)側でしか動かないJSフレームワークなどによるレンダリングをサーバー側で行うことによって、初期表示時の高速読み込みや検索エンジン最適化(SEO)を向上するもの。

サーバー側でJSの処理を実行しようとしているときに、ブラウザ側にしか存在しないグローバルオブジェクトのwindowdocumentを参照しようとすると、標題のエラーが起こります。

対策

1. componentDidMount()内で処理を行う

componentDidMount()内に書いた処理は、基本的にcomponentがmountされた後にクライアント側のみで起こる処理なので、このエラーは起こらないようです。

ただ、componentDidMount()はクラスコンポーネントのライフサイクルメソッドなので、関数コンポーネントでは使えません。

2. if文で条件分岐

React Hooksが主流の現在、こちらの方が汎用性が高いです。

ssr1.js
if (process.browser) {
  // windowやdocumentを使う処理を記述
}

もしくはシンプルに:

ssr2.js
if (window) {
  // windowを使う処理を記述
}

なおundefinedを判定基準として使うことは一般的にアンチパターンとされるため、下記は避けたほうがいいと思われます。

ssr3.js
if (typeof window !== "undefined") {
  // windowを使う処理を記述
}

参考

Stack Overflow - Window is not defined in Next.js React app

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

vue-good-tableの検索ボックスに前回の検索キーワードを入れる

vue-good-tableの検索ボックスに、ローカルストレージなどに保存しておいた前回の検索キーワードを入れておく方法です。

環境

  • Vue.js 2.6.11
  • vue-good-table 2.16.3

HTML

vue-good-tableタグ内のrefで任意の名前を与えます。

<vue-good-table
  ref="my-table"
  :columns="columns"
  :rows="rows"
  :search-options="{
    enabled: true
  }">
</vue-good-table>

Vue.js

mountedで先ほど名前を与えたコンポーネントを取得し、そのglobalSearchTermにローカルストレージなどから取得した前回の検索キーワードを渡します。ここでは「my initial value」を代入しています。

// in js
mounted() {
  this.$refs["my-table"].globalSearchTerm = "my initial value";
}

サンプル

See the Pen vue-good-table initial global search by Kamiyama (@MtDeity) on CodePen.

参考

Global Search reset · Issue #452 · xaksis/vue-good-table

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

JavaScript カレンダー

前回作成した祝日クラスを使って、サンプルとしてカレンダーを作ってみました。
飾りっ気は無いです。

HTML

index.html
<!DOCTYPE html>
<html lang='ja'>
    <head>
        <meta charset='utf-8'>
        <meta name="viewport" content="user-scalable=no,width=device-width,initial-scale=1">
        <title>Calendar</title>
        <script src='./calendar.js'></script>
        <script src='./holiday_class.js'></script>
        <link rel="stylesheet" href="./calendar.css">
    </head>

    <body>

        <div class='input'>
            <input id='year' type='text' value='' onchange='calendar(this.value)'><input id='prev' type='button' value='-' onclick='prev()'>
            <input id='reset' type='button' value='Reset' onclick='reset()'>
            <input id='next' type='button' value='+' onclick='next()'>
        </div>

        <div class='year'></div>

        <div class='month jan'></div>
        <div class='month feb'></div>
        <div class='month mar'></div>
        <div class='month apr'></div>
        <div class='month may'></div>
        <div class='month jun'></div>
        <div class='month jul'></div>
        <div class='month aug'></div>
        <div class='month sep'></div>
        <div class='month oct'></div>
        <div class='month nov'></div>
        <div class='month dec'></div>

        <div id='m'></div>
    </body>
</html>

JavaScript

calendar.js
const d = new Date();

const today = {
    year: d.getFullYear(),
    month: d.getMonth() + 1,
    day: d.getDate(),
};

function calendar(year = 0) {

    if(isNaN(year) || year < 1 || year > 9999 || year != Math.floor(year))
        year = today.year;

    const holiday = new Holiday(year).getHolidayOfYear();

    // 年
    document.querySelector('.year').innerHTML = `<div class='yearName'>${year}年</div>`;
    document.querySelector('#year').value = year;

    for(let month = 1; month <= 12; month++) {
        d.setFullYear(year, month - 1, 1);
        // 月ラベル
        let monthBlock = '';
        const monthName = d.toDateString().split(' ')[1].toLowerCase();
        monthBlock += `<div class='monthName'>${month}月</div>`;

        // 当月最終日
        d.setFullYear(year, month, 0);
        const lastDay = d.getDate();

        // 当月1日の曜日
        d.setFullYear(year, month -1, 1);
        const firstDayWeek = d.getDay();

        // 曜日ラベル
        monthBlock += "<div class='weekTop'>";
        for(let w = 0; w < 7; w++) {
            const weekName = ['', '', '', '', '', '', ''][w];
            const weekClass = w === 0 ? 'sun' : w === 6 ? 'sat' : '';
            monthBlock += `<div class='d ${weekClass}'><div class='t'>${weekName}</div></div>`;
        }
        monthBlock += "</div>";

        // 月初余白
        if(firstDayWeek) {
            monthBlock += "<div class='weekTop'>" + "<div class='d'></div>".repeat(firstDayWeek);
        }

        // 日付
        for(let day = 1; day <= lastDay; day++) {
            d.setFullYear(year, month - 1, day);
            const w = d.getDay();

            // 土日クラス名
            let weekClass = w === 0 ? 'sun' : w === 6 ? 'sat' : '';
            // 祝日クラス名
            if(holiday[month][day] !== undefined) weekClass += ' holiday';

            // 週初め
            if(w === 0) monthBlock += "<div class='weekTop'>";

            // 祝日名表示用
            const holidayTag = (holiday[month][day] !== undefined) ?
                `<a class='holiday' href='javascript:;' ` +
                    `onmousemove='p1("${holiday[month][day]}")' onmouseout='p0()'>` :
                '';
            monthBlock += holidayTag;

            // 当日クラス名
            const todayClass =
                (today.year === year && today.month === month && today.day === day) ?
                'today' : '';

            monthBlock += `<div class='d ${todayClass} ${weekClass}'><div class='t'>${day}</div></div>`;

            // 祝日閉じ
            monthBlock += (holidayTag !== '') ? '</a>' : '';

            // weekTopの閉じ
            if(w === 6 || day === lastDay) monthBlock += '</div>';
        }

        // DOM更新
        document.querySelector(`.month.${monthName}`).innerHTML = monthBlock;
    }
}

function p1 (str){
    const d = document.querySelector('#m');
    d.textContent = str;
    d.style.top = y + "px";
    d.style.left = x + "px";
    d.style.display = "block";
}

function p0 (){
    const d = document.querySelector('#m');
    d.style.display = "none";
}

let x, y;
onload = function() {
    calendar();
    document.onmousemove = function(e) {
        x = e.pageX - 20;
        y = e.pageY - 40;
    }
    document.ontouchstart = function(e) {
    var to = e.changedTouches[0];
        x = to.pageX - 20;
        y = to.pageY - 64;
    }
}

function prev() {
    calendar(Number(document.querySelector('#year').value) - 1);
}
function next() {
    calendar(Number(document.querySelector('#year').value) + 1);
}
function reset() {
    calendar(today.year);
}

CSS

calendar.css
input[type="text"] {
    width: 48px;
}
input[type="button"] {
    width: 25px;
}
input[type="button"]#reset {
    width: 52px;
}
.input {
    padding-bottom: 10px;
}
a.holiday {
    text-decoration: none;
}
.year {
    padding-bottom: 16px;
}
.yearName {
    margin: 0 auto 0 auto;
}
.month {
    line-height: 150%;
    padding-bottom: 16px;
    display: inline-grid;
    padding-right: 16px;
}
.monthName {
    margin: 0 auto 0 auto;
}
div.d {
    display: inline-grid;
    width: 26px;
}
.t {
    margin: 0 auto 0 auto;
    font-size: 14px;
}
.d.sat {
    color: #4444ff;
}
.d.sun {
    color: #ff4444;
}
.d.holiday {
    color: #ff4444;
    font-weight: bold;
}
.d.today {
    background: #ddddff;
}
#m {
    position: absolute;
    top: 0px;
    left: 0px;
    display: none;
    padding: 4px 12px 4px 12px;
    background-color: #ffffff;
    color: #882222;
    font-size: 14px;
    font-weight: bold;
    border: 1px #884444 solid;
}

他に前回の祝日クラスをholiday_class.jsとして保存したファイルを使います。

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

【Vue.js】コンポーネント $emit で 子 → 親 へデータを渡す

はじめに

$emitを使ったコンポーネント親子間のデータのやりとりがなかなかややこしく感じた。
なのでやったことを記録に残します。

$emit とは?
Vue.js リファレンス を読んでも正直よくわかりませんでした。
この親子間のデータのやりとりに関しては、
「子コンポーネントで指定したタイミングに、親コンポーネントで処理を起こさせることができる。その処理ついでにデータを渡す。」

みたいな感じで勝手に解釈しました。(わかりずらくてすみません)

どんなものを実装するか?

・子コンポーネントにボタンを設置。
・そのボタンをクリックすると親コンポーネントのdataの値(初期値0)に1加算された値が子コンポーネントから送られる。

実装してみる

Child.vue
<template>
  <!-- クリックでplus1メソッドを呼び出す -->
  <button @click="plus1">プラス1</button>
</template>

<script>
export default {
  props: ["childScore"],
  methods: {
    plus1() {
      // $emit('親コンポーネントで発火させる関数の名前', 処理の内容);
      this.$emit('plus-click', this.childScore + 1);
    }
  }
}
</script>
Parent.vue
<template>
  <!-- :child-score="score" で初期値を渡している。 -->
  <!-- そして@plus-clickイベントが発火したら、$event($emitで発火させたplus1メソッド処理後の値)をscoreに代入する -->
  <Child :child-score="score" @plus-click="score = $event"></Child>
  <p>{{ score }}</p>
</template>

<script>
import Child from "Child.vue"

export default {
  data() {
    return {
      score: 0,
    }
  }
}
</script>

最後に

props, $emit などコンポーネント間のデータやりとりなかなか難しい。
間違いなどなどあれば、気軽にご指摘ください。

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

React + TypeScript 環境構築 + ビルド環境を Flavor で切り替える (not create-react-app)

はじめに

この記事は React + TypeScript な環境を作成し、Flavor を設定してビルド環境( Development, Staging, Production )を切り替えられるような Project を作成することを目的としています。

React + TypeScript 環境の構築

初期設定

サックっと初期設定してしまいましょう。

$ mkdir react-flavor-example
$ cd react-flavor-example
$ npm init

色々聞かれるので、適当に質問に答えましょう。

package name: (app) react-flavor-example
version: (1.0.0)
description:
entry point: (main.tsx) webpack.config.js
test command:
git repository:
keywords:
author: yukitaka13-1110
license: (ISC)
About to write to /app/package.json:

Dependency の追加

React 系

$ npm i -D react react-dom @types/react @types/react-dom

Webpack 系

$ npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin

TypeScript 系

$ npm i -D typescript ts-loader

tsconfig.json の作成

tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "module": "commonjs",
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "experimentalDecorators": true,
    "target": "es5",
    "jsx": "react",
    "lib": [
      "dom",
      "es6"
    ]
  },
  "include": [
    "src"
  ],
  "compileOnSave": true
}

webpack.config.js の作成

webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',

  entry: './src/main.tsx',
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
      },
    ],
  },
  resolve: {
    extensions: [
      '.ts', '.js', 'tsx',
    ],
  },
};

index.html の作成

public/ 以下に index.html を作成します。

public/index.html
<html lang="ja">
  <body>
    <div id="root" />
  </body>
</html>

main.tsx の作成

src/ 以下に main.tsx を作成します。

src/main.tsx
import * as React from "react";
import * as ReactDOM from "react-dom";

const App: React.FC = () => {
  return <div>React Flavor Example</div>
};

ReactDOM.render(<App />, document.getElementById("root"));

この時点でディレクトリ構造が以下のようになっていればOKです。

$ tree ./ -L 2

./
├── node_modules
├── package-lock.json
├── package.json
├── public
│   └── index.html
├── src
│   └── main.tsx
├── tsconfig.json
└── webpack.config.js

package.jsonscripts にコマンドを追加します。

package.json
{
  ...
  "scripts": {
    "start": "webpack-dev-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ...
}

npm run start で起動して http://localhost:8080/ へアクセスして React Flavor Example という文字が表示されることを確認

スクリーンショット 2020-07-14 01.19.36.png

Flavor の設定

ここからが本題です。
アプリで利用する環境変数を切り替えたいということは良くあることだと思います。
node.js では process.env.NODE_ENV という環境変数を利用することでアプリで利用する環境変数を切り替えることができるようです。
しかし、process.env.NODE_ENVstaging を受け取ることができません。型定義を見る感じは受け取れそうですが、どうやらdevelopment | production | test 以外は受け付けないようにないっているみたい...。(良く分かっていない)

@types/node
interface ProcessEnv {
    [key: string]: string | undefined;
}

そもそも型定義も貧弱なのでそこも含めて解決します。

process.env の型を上書きする

process.env の型を上書きするには、プロジェクト内のどこかに global.d.ts ファイルを作成し、以下のように書くことで可能です。

src/types/global/global.d.ts
/// <reference types="node" />

declare namespace NodeJS {
  interface ProcessEnv {
    readonly NODE_ENV: 'development' | 'production' | 'test';
    readonly FLAVOR: 'development' | 'staging' | 'production';
  }
}

NODE_ENV は触っちゃいけない気がしたので以下のように FLAVOR を生やしてみました。

その後、package.jsonFLAVOR を渡す scripts を追加します。

package.json
{
  "scripts": {
    "start": "webpack-dev-server",
+   "dev": "FLAVOR=\"development\" webpack-dev-server",
+   "stg": "FLAVOR=\"staging\" webpack-dev-server",
+   "prod": "FLAVOR=\"production\" webpack-dev-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
}

渡しただけではアプリ側では受け取れないので、webpack.config.js で工夫します。

webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const webpack = require('webpack');
+ const flavor = process.env.FLAVOR || 'development';

module.exports = {
  mode: 'development',

  entry: './src/main.tsx',
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
    }),
+   new webpack.DefinePlugin({
+     'process.env.FLAVOR': JSON.stringify(flavor),
+   })
  ],
}

main.tsxconsole.log() を追加してみましょう。
先程、型定義を上書きしたので補完が効くようになっています。

スクリーンショット 2020-07-14 01.56.01.png

試しに npm run stg を実行してみましょう。
ちゃんと値を受け取れています。

スクリーンショット 2020-07-14 01.57.50.png

後は適当に環境毎に変数を定義して process.env.FLAVOR を使って分岐するだけです。
例えば以下のような config ディレクトリを用意しておけば良いでしょう。

./src/config
├── development
│   └── index.ts
├── index.ts
├── production
│   └── index.ts
└── staging
    └── index.ts
staging/index.ts
import { EnvVariables } from '../';

const stgVariables: EnvVariables = {
  HOGE: 'STAGING_HOGE',
  FUGA: 'STAGING_FUGA',
};

export default stgVariables;
index.ts
import devVariables from './development';
import stgVariables from './staging';
import prodVariables from './production';

export type EnvVariables = {
  HOGE: string;
  FUGA: string;
};

const envVariables = (): EnvVariables => {
  if (process.env.FLAVOR === 'production') {
    return prodVariables;
  }
  if (process.env.FLAVOR === 'staging') {
    return stgVariables;
  }
  return devVariables;
};

export default envVariables();
src/main.tsx
import * as React from "react";
import * as ReactDOM from "react-dom";
import envVariables from './config' // Add import;

const App: React.FC = () => {
  return (
    <div>
      <div>React Flavor Example</div>
      <div>{envVariables.HOGE}</div>
      <div>{envVariables.FUGA}</div>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));

npm run stg を実行して http://localhost:8080/ にアクセスします。

スクリーンショット 2020-07-14 02.22.48.png

ちゃんと値を表示することができました。

終わりに

今回使用したコードはこちらに載せています。
https://github.com/yukitaka13-1110/react-flavor-example

現在、QUANDO では iOS, Android, Flutter エンジニア募集中です。

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

JavaScriptでアラートを表示する方法!初心者向けに簡単に解説 byウェブカツ

WEBサイトを閲覧をしていると、エラーを表示したり何らかの確認を求めるポップアップが表示されたことがあるのではないだろうか。JavaScriptにはそのようなポップアップを表示させるためのalertという機能が存在する。

そこで今回は、

・ポップアップを表示させたいけどやり方がわからない
・ポップアップにどんなものが表示できるか知りたい

という人のために、

・alertの書き方
・alertで表示できる値

を紹介する。

ポップアップを表示させるのは実はとても簡単。ぜひチャレンジしてほしい。

アラートとは?


アラートとは、ユーザーへ確認を求める目的であったり、何らかの注意喚起をするためにポップアップを表示すること。WEBサイトを見ていると、以下のようなポップアップ画面を目にしたこともあるのではないだろうか。

WEBページを作る上ではよく使う機能なので、覚えておくことをおすすめする。

alertの書き方


JavaScriptでアラートを表示させたい時は、<script>タグの中に以下のように記述するだけで簡単に表示できる。

alert( 表示させたい値 );

この値には文字列や数値など好きな値を設定できる。

試しに「アラート」という文字列を表示させてみよう。

html
<!DOCTYPE html>
 <html>
  <head>
   <meta charset="utf-8">
   <title>JavaScript</title>
  </head>
  <body>
  <script>
   alert('アラート');
  </script>
  </body>
 </html>

このようにコードを書いたhtmlファイルをブラウザで開いてみると、ブラウザ自体には何も表示されないが以下のようなポップアップが表示される。

alert()に渡す値

alert( 表示させたい値 );

に表示させたい値を設定することで、ポップアップに表示することができる。ただし、何を表示させるかで書き方が異なる。

次にそれぞれの書き方を紹介する。

文字列を出力


alertに「文字列」を表示する場合は、ダブルクォーテーションマーク「"」かシングルクォーテーションマーク「'」で文字列を囲む。
javascript
alert('アラートのメッセージ');

数値を出力

alertに「数値」を表示したい場合は、以下のように半角で数値をそのまま指定する。

javascript
alert( 123 );

ちなみに、数値を「"」「'」で囲むと数値ではなく文字列として認識されるようになる。

文字列を連結させて出力する


alert()に「文字列」「数値」など組み合わせたい値をバラバラに渡し、連結したものを表示させることもできる。データを文字列として連結させたい場合は「+」で間を繋ぐ。
javascript
alert( '今日は' + 10 + '日です。' );


計算式の結果を出力する


alert()に計算式を指定すると、計算結果がポップアップに表示される。

以下のように記述すると、

javascript
alert( 100 + 10 );

と計算結果の「110」がポップアップに表示される。

足し算のほか、「-」で引き算や「*」で掛け算も計算できる。

変数を使ってメッセージを出力する


alert()に変数を指定して、変数の中身をポップアップに表示することもできる。

以下のように記述すると、

javascript
var deleteMessage = '本当に削除しますか?';
alert(deleteMessage);

このように変数「deleteMessage」の中身がポップアップに表示される。

confirmで確認ダイアログを作成する


confirmを使うことで、ユーザーが「OK」か「キャンセル」を選べる確認ダイアログが作成できる。

例えば以下のように記述すると、

html
<!DOCTYPE html>
 <html>
  <head>
   <meta charset="utf-8">
   <title>JavaScript</title>
  </head>
  <body>
   <script>
    function deleteMessage() {
    confirm("本当に削除しますか?");
    }
   </script>
   <p><button onclick="deleteMessage()">削除</button></p>
  </body>
 </html>

このように削除ボタンを押した時に「本当に削除しますか?」というメッセージが入った確認ダイアログが表示される。

confirm()表示するメッセージを設定し、onclickクリックした時にconfirmで設定した内容が実行されるようになっている。

まとめ

以上、JavaScriptでアラートを表示する方法について、

・alertの具体的な書き方
・alertで表示できる値と注意点
・confirmで確認ダイアログを表示する方法

を紹介した。

アラートを使いたい場面に合う方法で使ってみてほしい。


かずきち

プログラミング学習サイト「ウェブカツ!!」の顧問。
不動産、保険の営業マンから、エンジニアへ転身。
「HTMLって何?」という状態から3ヶ月の独学のみでエンジニアへ転職し、1年で年収1千万を稼ぐエンジニアへ。
独学時代のプログラミング学習の分かりにくさや、「技術しか出来ずに稼げていないエンジニア」の現状を変えるため「ウェブカツ」を立ち上げ運営している。

【ウェブカツ公式WEBサイト】
https://webukatu.com/

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

Rails6でJavaScriptが読み込まれない

はじめに

Rails6のアップデートで、JavaScriptを設置する場所や設定方法が
Rails5以前とは変わったらしいので、Rails6でJavaScriptを使う方法について

実行環境
gem 'rails', '~> 6.0.3', '>= 6.0.3.2'

すぐ解決させたい方は、
@sasakura_870さんの下記リンクへ飛んでいただくか
【Rails】RailsでjQueryを使う方法とJavaScriptの実行順序はめちゃ大事だという話
一番下までスクロールして「翌日に訂正 こちらで解決」の記載からご覧ください。

読み飛ばしていいです

最近、Rails6へアップデートしたのですが、
以下の”function.js”が読み込まれず・・swiperが動かないよ!
※そもそもjsの機能、swiperを取り込むために今回はjsファイルを読み込む必要がありました。
しかし今回railsでjs読み込むのがお初なのに、rails6では設定方法が変わってるとのこと・・まじか。
※swiperの設定方法については下記を参考にしました。
https://qiita.com/miketa_webprgr/items/0a3845aeb5da2ed75f82

このfunctionファイルを読みたい

スクリーンショット 2020-07-14 1.00.53.jpg

/app/javascripts/application.js

本来は・・以下の記載により/assets/javascripts/内の全てのjsファイルを読み込んでくれるようです。
//= require_tree .
ん?記述してあるのに・・なんで読み込まれないのかな

application.js
//
//= require bootstrap
//= require turbolinks
//= require_tree .

application.html.erb

うーん、ちゃんとCDNのincludeもしてるんだけど??

application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>

    <link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.css">
    <script src="https://unpkg.com/swiper/swiper-bundle.js"></script>

公式ドキュ読むの苦手な自分はQiitaの記事を色々読み漁り・・ふー。。

ようやく解決

[修正1](どなたかの記事より・・)
どうやらRails6.0でwebpackerが追加された(表現あってるのかわからないけど)ことによって、
webpackerがJavascriptをコンパイルできるようにしてあげなければいけないらしい。

エラー部分のコード
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
改コード
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

[修正2](どなたかの記事より・・)
jsファイル読み込みも"javascript_pack_tag"で指定してあげます。

view/swiper.html.erb
<h1>swiper</h1>
<-- jsのコードを記述 -->

<%= javascript_pack_tag 'function' %>

注意
上記の方法ですと・・assets/javascripts配下のjsは読み込まれませんでした。

app/javascript/packs/function.jsに置いてあげたところ読み込んでくれたので
やっとjs動いた。。と思って安心して眠りについたのですが・・

※翌日に訂正  こちらで解決

あとjavascript専用のgemでも入れなきゃいけないのかな?と思いググってはいたのですが
こちらってjavascriptも読んでくれるのでしょうか・・jquery-rails
元々このgemは入れていたので・・原因は掴めていません。やり方ありそう

翌日・・
いや、すみません。今回もっと簡単な方法で解決しました・・
@sasakura_870さんの
【Rails】RailsでjQueryを使う方法とJavaScriptの実行順序はめちゃ大事だという話

gem 'jquery-rails'を入れている(bundle installしている)状況で
下記"//= require jquery_ujs"の記述をするだけで
"javascript_include_tag"だとしても読み込んでくれました・・

/app/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require_tree .

※assets配下のjsファイルを読み込んでくれましたので、こちらのほうが正しい気がします。

つまり・・

application.html.erb
<head>
・・・
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
・・・
</head>
view/swiper.html.erb
<h1>swiper</h1>
<-- jsのコードを記述 -->

<%= javascript_include_tag 'function' %>

これでassets配下読み込んでくれました・・

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

<JavaScript>特定の文字列で区切られたファイル名をそのファイル名の一部で一括変換するクイズ

背景

やりたいこと

  • フォルダの中にabc_test_ok_1_this.csv・・・abc_test_ok_100_this.csvのファイルが入っている
  • これらを1.csv・・・100.csvにリネームしたい
  • ということだと思う(斜め読みしたから違うかも)
  • これらをコマンドラインでワンライナーで実現するというもの

環境

node -v
# v14.5.0

事前準備

  • 元記事はabc_test_ok_1_this.csv・・・abc_test_ok_100_this.csvのファイルを用意するところからワンライナーでやってるのでそこから
node -e "const fs=require('fs');[...Array(100).keys()].forEach(i=>fs.writeFile('abc_test_ok_'+(i+1)+'_this.csv','',()=>{}));"
  • nodeコマンドはnode main.jsのようにしてファイルを実行できるが、node -e "console.log('Hello')"のように-eオプションをつけるとその後をEvalするのでファイルに書かなくても直接実行できる
  • ちゃんと改行するとこんな感じ
const fs = require('fs');

[...Array(100).keys()].forEach(i => {
  fs.writeFile(`abc_test_ok_${i + 1}_this.csv`, '', () => {});
});
  • [...Array(100).keys()]で要素数100個の配列を作っています
    • .kyes()としてるので配列の各要素には0〜99の値が格納されます
    • [0, 1, 2, ... 98, 99]といった具合
  • これをforEachしてるので100回ループが回ってます
  • ファイルの作成にはfs.writeFile('ファイル名', 'ファイルの内容', callback)を使っています
    • ファイル名はお題の名前にしてindexを埋め込んでいる
    • ファイルの中身は指定がなかったので空っぽに
    • コールバックはとりあえず今回はいらないので空の関数
  • という感じで実行するとabc_test_ok_1_this.csv・・・abc_test_ok_100_this.csvが作成されました

ファイルのリネーム

  • ここが本題でしょうか
  • フォルダの中にあるabc_test_ok_1_this.csv・・・abc_test_ok_100_this.csvのファイルを1.csv・・・100.csvにリネームします
node -e "const fs=require('fs');fs.readdir(__dirname,(error,files)=>{files.forEach(file=>{fs.rename(file,file.match(/abc_test_ok_(.*)_this.csv/)[1]+'.csv',() => {},);});});"
  • さっきと同じくnodeコマンドの-eオプションを使ってワンライナーで実現しています
  • 改行するとこんな感じ
const fs = require('fs');

fs.readdir(__dirname, (error, files) => {
  files.forEach(file => {
    fs.rename(file, file.match(/abc_test_ok_(.*)_this.csv/)[1] + '.csv', () => {});
  });
});
  • fs.readdir('ディレクトリ', callback)を使ってディレクトリの中のファイル一覧を取得しています
    • 第1引数はカレントディレクトリでいいので__dirnameを定義
    • 第2引数はファイル名の一覧を処理する関数を定義
  • filesにファイル名の一覧が入っているのでfiles.forEach(file => { ... }でループを回しています
  • fs.rename('変更前のファイル名', '変更後のファイル名', callback)を使ってリネームしています
    • 変更前のファイルはループで回ってくるfile
    • 変更後のファイル名は正規表現で必要な部分を引き抜いています
    • callbackは今回は不要なので空の関数
  • これでフォルダの中にあるabc_test_ok_1_this.csv・・・abc_test_ok_100_this.csvのファイルを1.csv・・・100.csvにリネームできました
    • 余計なファイルがあったときの処理とかはいったん無視しました

終わりに

  • ちょっとしたスクリプトを書くのは息抜きの暇つぶしにいいですね。
  • Nodeでファイルの読み書きなんてほとんどやったことないのでググりながらやりました。もっとかっこいい書き方あるかも。
  • 次は誰かPythonとかRubyとかでお願いします。

参考

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