20201113のJavaScriptに関する記事は23件です。

expoで作成したreact nativeアプリをios向けにテスト配布する方法

はじめに

expoで作成したiOSアプリを開発者以外に配布したいという場面で苦戦したのでまとめました。

主に方法は2つ (他にもあればご教授お願い致します)

expoでpublishする(無料)
Testflight/DeployGateを利用する(Apple Developer Programへ入る必要あり)

expoでpublishする(無料)

expo publishコマンドを実行するとアカウントのProjectページにビルドされたアプリが表示されます。

ここで詰まったのが、Projectページに表示されているQRコードをスキャンしたが読み込みに失敗した。

image.png

QRコードの真下に書いてあるのだが、この機能が使えるのはandroid端末のみ。

iOSはpublish先のexpoアカウントにログインしていないと使えないらしい。
チームの共有のアカウントを作って、そこにpublishするのが良いかと思います。

TestFlightやDeployGateを利用する

.ipaファイルをアップロードする事でテスト配布する事ができます。

expo build:ios

を実行すれば.ipaファイルが作成できますが、Apple Developer Program(年会費$99が必要)に入会していなければ実行できません。

DeployGate
TestFlight

まとめ

自分は今回ハッカソン用に配布したかっただけで、Developer Programに入る程ではなかった為アプリの配布に少し苦戦しました。
同じexpoアカウントに複数人でログインするというのに少し抵抗がありましたが、調べてみた結果これ以外方法が無いかなと思いました。(ハッカソンという時間の問題もあり)

ストアへの公開まで考えている方はApple Developer Programに入るのが確実かなと思います。

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

【JavaScriptの超基本】関数や戻り値、スコープについて簡単に解説

概要

この記事では、JavaScriptの関数や戻り値、スコープについて、超基本的な知識をメモ的にまとめています。
自分用の備忘録なのであしからず。

目次

関数とは

関数」とは、簡単に言うと複数の処理をまとめたものです。

例えば、2つの数値を足す、足し算の結果を出力するというような処理を、関数(ここではaddとします)の中で定義することで、addを呼び出すだけで2つの処理を行ってくれます。

それでは、関数の定義から使い方までをみていきましょう。

関数の定義

関数の定義と呼び出し方の構文は、以下のようになっています。

index.js
//関数の定義
function 関数名(引数) {
    処理1
    処理2
     :
     :
}

//関数を呼び出す

関数名(引数);

functionに続けて関数名を書き、( )の中に引数を書きます。{ }の中には、関数を呼び出した際に実行する処理をを書きます。

ここで出てきた「引数」とは、関数に与える情報です。関数を呼び出すときに一緒に値を渡すことで、関数の中でその値を使うことができます。

定義した関数は関数名(引数);とすることで呼び出すことができます。

実際に書いてみるとこのようになります。自己紹介の関数を書いてみました。

index.js
function introduce(name, job) {
    console.log(`私は${name}です。`)
    console.log(`${job}です。`)
}

introduce('ta1fukumoto', 'エンジニア');
//私はta1fukumotoです。
//エンジニアです。

introduce('Messi', 'フットボールプレイヤー');
//私はMessiです。
//フットボールプレイヤーです。

コードをみていきます。

まず、function introduce(name, job) {}introduceという名前の関数を定義しています。
introduce関数は引数にnamejobを受け取ります。そのため、関数を呼び出す際に、2つの引数を渡す必要があります。

introduce関数の中では、2つの処理が書かれています。関数を呼び出した際にこれらの処理がまとめて実行されます。
これらの処理の中では、引数が使われており、関数を呼び出した際に渡された引数の値が代入されます。

introduce('ta1fukumoto', 'エンジニア');の部分で関数を呼び出しています。引数で渡しているta1fukumotonameに、エンジニアjobに入り、関数の中の処理が実行されます。

関数式

関数は、定数や変数に代入することができます。これを「関数式」と言います。

関数式は、「無名関数」とすることができ、関数名をつけなくても良くなります。

関数式の構文は、以下に示す通りです。

index.js
//関数式による関数の定義
const 定数 = function(引数) {
    処理1
    処理2
     :
     :
}

//関数式を呼び出す
定数(引数);

このように、定数や変数を定義し、そこに関数を代入する感じです。このとき関数名は省略することができます。
呼び出す際は、定数の後ろに(引数)と書いて、引数を渡します。

先ほどの例を関数式で書き換えると次のようになります。

index.js
const introduce = function(name, job) {
    console.log(`私は${name}です。`)
    console.log(`${job}です。`)
}

introduce('ta1fukumoto', 'エンジニア');
//私はta1fukumotoです。
//エンジニアです。

introduce('Messi', 'フットボールプレイヤー');
//私はMessiです。
//フットボールプレイヤーです。

関数名が定数に置き換わったようなイメージですね。

アロー関数

関数式による関数の定義の際、function()と書いてきましたが、この部分を() =>としても関数を定義することができます。
この関数の書き方を「アロー関数」と呼びます。

アロー関数を使うと、コードがシンプルになり読みやすくなります。

引数を渡すときは、(引数) =>のように()の中に引数を書きます。

index.js
const introduce = (name, job) => {
    console.log(`私は${name}です。`);
    console.log(`${job}です。`);
}

アロー関数では、関数の定義部分が変わるだけで、呼び出し方は変わりません。

アロー関数は、. ES2015(ES6)から利用できるようになった表現です。1行で書く際の省略形などもありますが、ここでは割愛させていただきます。

オブジェクトと関数

関数は、オブジェクトの値として用いることもできます。
関数を呼び出すときは定数名.プロパティ名()とします。

index.js
//オブジェクトの値として関数を定義する場合
const 定数名 = {
     プロパティ名: (引数) => {
          処理
     }
};

//関数を呼び出す
定数名.プロパティ名(引数);

戻り値

戻り値」とは、呼び出し元で受け取る処理結果のことで、このことを「関数が戻り値を返す」と言います。

戻り値を返すには、関数の中でreturnを使います。return 値と書くことで、関数はその値を戻り値として返します。

呼び出した関数に戻り値がある場合、関数の呼び出し部分がそのまま戻り値に置き換わります。そのため、関数の呼び出し部分を定数に代入することもできます。

戻り値として、面積の計算結果を返すコードを書いてみます。

index.js
//戻り値として面積を計算した値を返す関数
const area = (height, width) => {
    return height * width;
};

//関数をそのまま出力
console.log(area(12, 5));
//60

//関数を別の定数に代入して出力
const result = area(12, 5);
console.log(result);
//60

コードをみていきましょう。

関数areaはアロー関数の関数式で定義されています。引数にはheightwidthを受け取ります。

関数の中の処理では、計算結果をreturnで返しています。

そのため、関数を呼び出してコンソールで出力すると、計算結果の60が表示されます。

関数はさらに定数に代入することもでき、代入した定数をconsole.logで出力すると計算結果が表示されます。

条件式をreturnする

条件式をreturnすると、真偽値を戻り値として返すこともできます。

index.js
const checkArea = (height, width) => {
    const area = height * width;
    return area > 50;
};

console.log(checkArea(3,7));
//false

console.log(checkArea(12, 5));
//true

上の例では、areaを計算したあと50より大きいか判定し、真偽値を戻り値として返します。
そのため、出力はtrueやfalseとなります。

最後に、returnは戻り値を返すだけでなく関数の処理を終了させる性質も持っています。
よってreturnの後にある処理は実行されません。

スコープ

スコープ」とは、それぞれの定数や変数が使用できる範囲のことです。

ひとつ前の例で、定数 areaを関数内で定義しています。areaを関数の{ }の外で出力しようとするとエラーが表示されます。このように、関数の引数や関数内で定義した定数や変数は、その関数の中でしか使うとこができません。

関数だけではなく、if文やswitch文などの条件文や、for文などの繰り返し文モスコープを作ります。基本的に{ }を使う構文がスコープを作ると考えましょう。

index.js
const calculateArea = (height, width) => {
    const area = height * width;
    console.log(area); //スコープの中なのでOK
};

calculateArea(2, 6);
//12

console.log(area); //スコープの外なのでNG
//エラーが表示されます

例のコードをみていきましょう。

定数areaは関数の中で定義されています。よって、areaは、関数calculateAreaの中でしか使うことができません。
関数の外で、areaconsole.logで出力しようとするとエラーが生じます。

一方、関数の外で定義した定数や関数は関数の中でも外でも使うことができます。

まとめ

以上、関数についてまとめました。
今回も学んだ知識を使って簡単なプログラムを書いてみました。

index.js
//背番号からバルサの選手を特定します
const checkPlayerName = (number) => {
    switch (number) {
        case 10:
            return 'Messi';
        case 3:
            return 'Pique';
        case 21:
            return 'deJong';
        default:
            return '選手が見つかりません';
    }
};

console.log(checkPlayerName(10));
//Messi

console.log(checkPlayerName(3));
//Pique

console.log(checkPlayerName(99));
//選手が見つかりません

トップチーム全員文でやろうと思いましたが、流石にコードが長くなりすぎるのでやめました笑
このように関数の中にswitch文を書くこともできます。また、returnがbreakの代わりになっています。

最後まで読んでいただきありがとうございます。ではまた。

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

chrome拡張機能の開発記録2

先日、自身の二作目であるchrome拡張機能『YouTube Video Tag』をリリースしました。

今回の記事では、前回の記事には載っていない新たな学びを書き記していこうと思います。

※以下の本文では、私が作った拡張機能のソースコードを引用している所があります。全てを載せると長くなる部分は省いていますが、詳細を見たい方はChrome extension source viewerなどのツールを使うか、拡張機能をインストールしてローカルストレージのファイルを見るなどして下さい(GitHubのリポジトリは公開してないので)。


オブジェクトのディープコピーを作る

オブジェクトのディープコピーを作るには、文字列に変換(JSON.stringify)してから元に戻します(JSON.parse)。

let goods = {
    "name": "pen",
    "price": 150
};
let goods_copy = JSON.parse( JSON.stringify(goods) );

goods["price"] = 200;
console.log(goods_copy["price"]); // > 150

コールバック関数内からインスタンスを参照する

メソッド内でイベントリスナーを定義する際、コールバック関数内でthisを使うと、インスタンスではなくdocumentを指すことになり、メンバーが見つかりません(undefinedになる)。

class Hoge {
    name = "hoge";

    piyo(){
        $('#fuga-btn').on('click', function(){
            console.log (this.name); // > undefined
        });
    }
}

コールバック関数をアロー関数で定義すれば、その中で使われたthisは関数が定義された場所におけるthis――つまりインスタンスを指します。

class Hoge {
    name = "hoge";

    piyo(){
        $('#fuga-btn').on('click', () => {
            console.log (this.name); // > hoge
        });
    }
}

onメソッドの処理は重複する

jQueryのonメソッドを使ったイベントリスナーの追加は、重複します
重複させたくない場合は、onメソッドの前にoffメソッドを呼んで、既に追加されているイベントリスナーを削除する必要があります(追加されていなくてもエラーが出ることはありません。)。

例えば、私が作った拡張機能には以下のような関数があります。
「タグ要素をページに追加し、それぞれについている『削除』ボタンにイベントリスナーを追加する」という処理を行います。

addVideoTagsElem(tags){
    let tagGroupElem = $("#yvt-tag-group");
    for (let tag of tags) {
        let tagElem = /* 略 */;
        tagGroupElem.append(tagElem);
    }
    $('.yvt-btn-delete').off('click');
    $('.yvt-btn-delete').on('click', e => {
        // 略
    });
}

この関数は、タグの追加が行われる度に呼び出されます。
onメソッドを呼び出す対象要素をクラスで指定しているので、タグの追加が複数回行われると、前に追加されていたタグ要素にもイベントリスナーが追加され、重複してしまいます。

そこで、onメソッドを呼ぶ直前にoffメソッドを呼び、既存のタグ要素に追加されているイベントリスナーを削除しています。


タグ入力用ライブラリについて

私が作った拡張機能では、タグを入力する場所が二箇所あり、それぞれでタグ入力用ライブラリを使い分けています。

tagifytagsinputです。

最初はtagifyだけを使う予定でしたが、tagifyの不具合が原因で片方では使えなくなったので、別のライブラリも使うことにしました。

以下では、それぞれのライブラリを使ってみた感想などを述べていこうと思います。

tagify

tagifyはUIがキレイで、オプションなども充実していて、拡張性も高いライブラリです。
日本語の入力補完にも対応していて、delimiterの値を変更することで、全角スペース区切りにも対応します。

しかし、chrome拡張機能で使うとなったとき、いくつか問題が発生しました。

ダークテーマのページにあるテキストフォームにtagifyを適用すると、inputフォームの背景が黒くなり、入力した文字が見えなくなる。

この問題は、tagifyによって追加されたtagsタグの背景色を明示的に変更することで解決しました。

delimiterに全角スペースを指定していても、removeAllTagsメソッドでテキストフォーム内の全角スペースを削除できない。

確かに全角スペースを入力すると区切り文字として認識はされるのですが、半角スペースとは違い、全角スペースはテキストフォーム内にそのまま残ってしまいます。
この問題は、tagify.DOM.input.textContent = "";を実行し、テキストフォーム内に残った文字列を削除することで解決しました。

ページにショートカットキーが設定されている場合、tagifyを適用したテキストフォーム内でのキー入力が、ショートカットキーの機能を呼び出してしまう。

具体例を挙げると、YouTube動画ページ内にtagifyを適用したテキストフォームがある場合、その中でkjlmなどのキーを押すと、動画の再生が止まったり、シークバーが動いたり、ミュートになったりしてしまうということです。

keydownイベントが発生したらstopPropagationを実行するなど、色々試してみましたが、完全な解決には至らなかったので、私は「既存のページに追加したテキストフォームにtagifyを適用する」という使い方を断念しました。

ポップアップページでは問題なく使えました。

tagsinput

tagifyの代わりに動画ページで使うことにしたのが、このライブラリです。

特にこれにこだわる理由もないのですが、日本語の入力補完ができ、ショートカットキーが暴発することもなかったので、採用しました。

難点は、tagifyと違って全角スペースを区切り文字にできないことと、input要素のidが変更されるので、入力された値を取得するのが少しだけ面倒なことです。

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

Array.from

HTMLCollectionで配列系メソッドは使えないらしい

最近JSに触れる機会があるので、備忘録がてら。

const fields = document.getElementsByClassName(
    'elements'
  );

上記のように、getElementsByClassNameで取得できるHTMLCollectionは、配列系のメソッドを利用できないらしい。

そのため、Array fromを使って配列に変換
elements = Array.from(elements)

サンプル

配列系のメソッド今回の場合はforEachを使った実例。

const fields = document.getElementsByClassName(
    'elements'
  );
Array.from(elements).forEach((element) => {
    const teikies = field.getElementsByClassName(
      '
    );
    Array.from(teikies).forEach((teiki) => {
      link.addEventListener('click', () => {
        const inputs = field.getElementsByTagName('input');
        Array.from(inputs).forEach((input) => {
          const type = input.getAttribute('type');
          const masked = 'icoca';
          const unmasked = 'suica';
          if (type === 'password') {
            input.setAttribute('type', 'text');
            field.classList.remove(icoca);
            field.classList.add(suica);
          } else {
            input.setAttribute('type', 'password');
            field.classList.remove(suica);
            field.classList.add(icoca);
          }
        });
      });
    });
  });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#でjsonを読み込み辞書型に変換(強引)

お久しぶりです。

Qiitaの皆さん、お久しぶりです。
前回からかなり時間がアイてしまいましたが、あれから念願の新しいパソコンを購入しました。
そしてPythonにも飽きてきたので、C#に手を出してみることにしました。

ふと思ったjsonの読み込みについて

私は以前、Pythonでjsonを使用して設定ファイルやデータをイチイチに定義しなければならないものをjsonに格納して便利に使っていたので、ぜひC#でも使いたいと思い模索しました。
薄々気づいてはいましたが、Pythonはなんて素晴らしいライブラリを取り揃えているのかと思い知らされることになりました。

json読み込みのコード

hoge.json
{
  "hoge": "hoge1",
  "hogehoge": "hoge2",
  "hogehogehoge": "hoge3"
}

Pythonでのjsonの読み込みはjsonライブラリを使用しますが

import json

def load_json(filename):
    with open(filename) as files:
        load = json.load(files)

    return load

このように関数を一つ定義してしまえば、辞書型で帰ってくるので

result = load_json("hoge.json")

hoge1 = result["hoge"]
hoge2 = result["hogehoge"]
hoge3 = result["hogehogehoge"]

print(hoge1)
print(hoge2)
print(hoge3)

print(type(hoge1))
print(type(hoge2))
print(type(hoge3))

hoge1
hoge2
hoge3
<class 'str'>
<class 'str'>
<class 'str'>

こうすると簡単にstrやintで帰ってきますので、あとは煮るなり焼くなりすれば簡単に使うことができますが
(ガバコードデゴメンネ)

C#の場合

using System;
using System.IO;
using System.Text;
using System.Text.Json;

namespace ConsoleApp2
{
    class Program
    {
        public static string ReadAllLine(string filePath, string encodingName)
        {
            StreamReader sr = new StreamReader(filePath, Encoding.GetEncoding(encodingName));
            string allLine = sr.ReadToEnd();
            sr.Close();

            return allLine;
        }

        class hogejson
        {
            public string hoge { get; set; }
            public string hogehoge { get; set; }
            public string hogehogehoge { get; set; }
        }
        static void Main(string[] args)
        {
            string readjson = ReadAllLine("hoge.json", "utf-8");

            hogejson jsonData = JsonSerializer.Deserialize<hogejson>(readjson);

            string hoge = jsonData.hoge;
            string hogehoge = jsonData.hogehoge;
            string hogehogehoge = jsonData.hogehogehoge;

            Console.WriteLine(hoge);
            Console.WriteLine(hogehoge);
            Console.WriteLine(hogehogehoge);

            Console.WriteLine(hoge.GetType());
            Console.WriteLine(hogehoge.GetType());
            Console.WriteLine(hogehogehoge.GetType());

        }
    }
}
hoge1
hoge2
hoge3
System.String
System.String
System.String

こんな感じで記載しないとうまく読み込まないのですが、一番個人的に面倒なのが
「jsonのクラスを作らないといけない」
のと
「メソッド(Pythonでいう関数(若干違うけど))作った時わかりにくい!
ことが引っかかりました。

なぜこんなことになってしまったのか

Pythonは先程の関数で帰ってくる返り値は辞書型に対してC#は特定の返り値を指定しなければなりません。
このため、Pythonでは辞書型で帰ってくるので

hoge = result["hoge"]

とresultに指定定するだけでkeyである「hoge」のvalue「hoge1」を取り出すことができますが
対してC#は、例えば「hoge.json」のkeyである「hoge」のvalueが「hoge1」であったとき、クラス「hogejson」のhogeにstring型(str)でセットされます。
逆にこれをint型や別の型でセットしようとしてもできません。

なぜなら、クラスhogeの返り値はstringと決めているからです。
そのためPythonでよくお世話になった辞書型に返り値には指定されていないため、仮に返り値を"勝手"に辞書型に指定しても他の「hogehoge」や「hogehogehoge」のkeyはおろかvalueの情報すらありません(自己解釈)

ほんじゃあ辞書型に返ってくるように作ろう!笑

探してみたところ、C#には「Dictionaryクラス」というものが存在するようで、それでPythonの辞書型と同じように使えるみたいです。

なら話は早いです、hoge.jsonのkeyとvalueを辞書型に詰め込めるよう試行錯誤を繰り返してできたのがこちらです。

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;

namespace ConsoleApp2
{
    class library
    {
        // ファイル読み込み
        public string ReadAllLine(string filePath, string encodingName)
        {
            StreamReader sr = new StreamReader(filePath, Encoding.GetEncoding(encodingName));
            string allLine = sr.ReadToEnd();
            sr.Close();

            return allLine;
        }

        // List型をDictionary型に強引に変換する
        public static Dictionary<string, string> ListInDictionary(string[] left, string[] right)
        {

            // 一致したとき
            if (left.Length == right.Length)
            {

                var result = new Dictionary<string, string>();

                // leftのListの数の分だけforを回す
                for (int i = 0; i < left.Length; i++)
                {

                    // resultに辞書を追加
                    result.Add(left[i], right[i]);
                }

                return result;

            }

            // 不一致のとき
            else
            {
                return null;
            }
        }

        class hogejson
        {
            public string hoge { get; set; }
            public string hogehoge { get; set; }
            public string hogehogehoge { get; set; }
        }
        //hoge.jsonを読み込むためのメソッド
        public Dictionary<string, string> GetHogejson(string filename)
        {
            try
            {
                string jsonfile = ReadAllLine(filename, "utf-8");

                hogejson jsonData = JsonSerializer.Deserialize<hogejson>(jsonfile);

                string[] json_key = { "hoge", "hogehoge", "hogehogehoge" };
                string[] json_value = { jsonData.hoge, jsonData.hogehoge, jsonData.hogehogehoge };

                var result = ListInDictionary(json_key, json_value);

                return result;
            }

            // jsonが読み込めない時
            catch (JsonException)
            {
                return null;
            }

        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            library Library = new library();

            Dictionary<string, string> gethogejson = Library.GetHogejson("hoge.json");

            string hoge, hogehoge, hogehogehoge;

            hoge = gethogejson["hoge"];
            hogehoge = gethogejson["hogehoge"];
            hogehogehoge = gethogejson["hogehogehoge"];

            Console.WriteLine(hoge);
            Console.WriteLine(hogehoge);
            Console.WriteLine(hogehogehoge);

            Console.WriteLine(hoge.GetType());
            Console.WriteLine(hogehoge.GetType());
            Console.WriteLine(hogehogehoge.GetType());

        }
    }
}

...自分でも感じた、強引すぎると...

問題点

  1. valueがint型だろうとなんだろうとstring型にしてresult(辞書型)に格納する。
  2. 結局jsonデータのクラスを指定しなければならないので、根本的な解決には至っていない。
  3. とりあえず思いついた発想と知恵で強引に作ったので、見られたら怒られるくらいのガバコード

個人的にはPythonのように使えるのが一番ありがたいですが、あまりに強引すぎて怒られるような気がします。

ちなみに結果は

hoge1
hoge2
hoge3
System.String
System.String
System.String

こんなふうに表示されます。

最後に

この方法よりこっちのほうが使いやすいよ!ガバすぎだろもっとこうするべきだ等あれば、ご遠慮無くぜひコメントまで!
結構真剣に困っていたので、また時間があれば改良してみます。

久々に書ききりました。
おやすみなさい。

引用元

https://usefuledge.com/csharp-json.html
https://json2csharp.com/ (先程のjsonデータクラスをjsonデータをもとに作ってくれるサイト、めっちゃ便利)
Thank you!!!!!

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

C#でjsonを読み込み・そして辞書型に変換(強引)

お久しぶりです。

Qiitaの皆さん、お久しぶりです。
前回からかなり時間が空きすぎましたが、あれから念願の新しいパソコンを購入しました。
そしてPythonにも飽きてきたので、C#に手を出してみることにしました。

理解するメモとして書き残しておきます。
ガバな点はご容赦ください。

ふと思ったjsonの読み込みについて

私は以前、Pythonでjsonを使用して設定ファイルやデータをイチイチに定義しなければならないものをjsonに格納して便利に使っていたので、ぜひC#でも使いたいと思い模索しました。
薄々気づいてはいましたが、Pythonはなんて素晴らしいライブラリを取り揃えているのかと思い知らされることになりました。

json読み込みのコード

hoge.json
{
  "hoge": "hoge1",
  "hogehoge": "hoge2",
  "hogehogehoge": "hoge3"
}

Pythonでのjsonの読み込みはjsonライブラリを使用しますが

import json

def load_json(filename):
    with open(filename) as files:
        load = json.load(files)

    return load

このように関数を一つ定義してしまえば、辞書型で帰ってくるので

result = load_json("hoge.json")

hoge1 = result["hoge"]
hoge2 = result["hogehoge"]
hoge3 = result["hogehogehoge"]

print(hoge1)
print(hoge2)
print(hoge3)

print(type(hoge1))
print(type(hoge2))
print(type(hoge3))

hoge1
hoge2
hoge3
<class 'str'>
<class 'str'>
<class 'str'>

こうすると簡単にstrやintで帰ってきますので、あとは煮るなり焼くなりすれば簡単に使うことができますが
(ガバコードデゴメンネ)

C#の場合

using System;
using System.IO;
using System.Text;
using System.Text.Json;

namespace ConsoleApp2
{
    class Program
    {
        public static string ReadAllLine(string filePath, string encodingName)
        {
            StreamReader sr = new StreamReader(filePath, Encoding.GetEncoding(encodingName));
            string allLine = sr.ReadToEnd();
            sr.Close();

            return allLine;
        }

        class hogejson
        {
            public string hoge { get; set; }
            public string hogehoge { get; set; }
            public string hogehogehoge { get; set; }
        }
        static void Main(string[] args)
        {
            string readjson = ReadAllLine("hoge.json", "utf-8");

            hogejson jsonData = JsonSerializer.Deserialize<hogejson>(readjson);

            string hoge = jsonData.hoge;
            string hogehoge = jsonData.hogehoge;
            string hogehogehoge = jsonData.hogehogehoge;

            Console.WriteLine(hoge);
            Console.WriteLine(hogehoge);
            Console.WriteLine(hogehogehoge);

            Console.WriteLine(hoge.GetType());
            Console.WriteLine(hogehoge.GetType());
            Console.WriteLine(hogehogehoge.GetType());

        }
    }
}
hoge1
hoge2
hoge3
System.String
System.String
System.String

こんな感じで記載しないとうまく読み込まないのですが、一番個人的に面倒なのが
「jsonのクラスを作らないといけない」
のと
「メソッド(Pythonでいう関数(若干違うけど))作った時わかりにくい!
ことが引っかかりました。

なぜこんなことになってしまったのか

Pythonは先程の関数で帰ってくる返り値は辞書型に対してC#は特定の返り値を指定しなければなりません。
このため、Pythonでは辞書型で帰ってくるので

hoge = result["hoge"]

とresultに指定定するだけでkeyである「hoge」のvalue「hoge1」を取り出すことができますが
対してC#は、例えば「hoge.json」のkeyである「hoge」のvalueが「hoge1」であったとき、クラス「hogejson」のhogeにstring型(str)でセットされます。
逆にこれをint型や別の型でセットしようとしてもできません。

なぜなら、クラスhogeの返り値はstringと決めているからです。
そのためPythonでよくお世話になった辞書型に返り値には指定されていないため、仮に返り値を"勝手"に辞書型に指定しても他の「hogehoge」や「hogehogehoge」のkeyはおろかvalueの情報すらありません(自己解釈)

ほんじゃあ辞書型に返ってくるように作ろう!笑

探してみたところ、C#には「Dictionaryクラス」というものが存在するようで、それでPythonの辞書型と同じように使えるみたいです。

なら話は早いです、hoge.jsonのkeyとvalueを辞書型に詰め込めるよう試行錯誤を繰り返してできたのがこちらです。

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;

namespace ConsoleApp2
{
    class library
    {
        // ファイル読み込み
        public string ReadAllLine(string filePath, string encodingName)
        {
            StreamReader sr = new StreamReader(filePath, Encoding.GetEncoding(encodingName));
            string allLine = sr.ReadToEnd();
            sr.Close();

            return allLine;
        }

        // List型をDictionary型に強引に変換する
        public static Dictionary<string, string> ListInDictionary(string[] left, string[] right)
        {

            // 一致したとき
            if (left.Length == right.Length)
            {

                var result = new Dictionary<string, string>();

                // leftのListの数の分だけforを回す
                for (int i = 0; i < left.Length; i++)
                {

                    // resultに辞書を追加
                    result.Add(left[i], right[i]);
                }

                return result;

            }

            // 不一致のとき
            else
            {
                return null;
            }
        }

        class hogejson
        {
            public string hoge { get; set; }
            public string hogehoge { get; set; }
            public string hogehogehoge { get; set; }
        }
        //hoge.jsonを読み込むためのメソッド
        public Dictionary<string, string> GetHogejson(string filename)
        {
            try
            {
                string jsonfile = ReadAllLine(filename, "utf-8");

                hogejson jsonData = JsonSerializer.Deserialize<hogejson>(jsonfile);

                string[] json_key = { "hoge", "hogehoge", "hogehogehoge" };
                string[] json_value = { jsonData.hoge, jsonData.hogehoge, jsonData.hogehogehoge };

                var result = ListInDictionary(json_key, json_value);

                return result;
            }

            // jsonが読み込めない時
            catch (JsonException)
            {
                return null;
            }

        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            library Library = new library();

            Dictionary<string, string> gethogejson = Library.GetHogejson("hoge.json");

            string hoge, hogehoge, hogehogehoge;

            hoge = gethogejson["hoge"];
            hogehoge = gethogejson["hogehoge"];
            hogehogehoge = gethogejson["hogehogehoge"];

            Console.WriteLine(hoge);
            Console.WriteLine(hogehoge);
            Console.WriteLine(hogehogehoge);

            Console.WriteLine(hoge.GetType());
            Console.WriteLine(hogehoge.GetType());
            Console.WriteLine(hogehogehoge.GetType());

        }
    }
}

...自分でも感じた、強引すぎると...

問題点

  1. valueがint型だろうとなんだろうとstring型にしてresult(辞書型)に格納する。
  2. 結局jsonデータのクラスを指定しなければならないので、根本的な解決には至っていない。
  3. とりあえず思いついた発想と知恵で強引に作ったので、見られたら怒られるくらいのガバコード

個人的にはPythonのように使えるのが一番ありがたいですが、あまりに強引すぎて怒られるような気がします。

ちなみに結果は

hoge1
hoge2
hoge3
System.String
System.String
System.String

こんなふうに表示されます。

最後に

この方法より「こうしたほうがいいよ~・ガバすぎこれを使え」等あれば、ご遠慮無くぜひコメントまで!
結構真剣に困っていたので、また時間があれば改良してみます。

追記
これなんなんですかね?よく出てきて動かなくなります。
こうなるともう保存して起動し直すしかないのでめっちゃ不便です。Screenshot_11.png

久々に書き切りました。
おやすみなさい。

引用元

https://usefuledge.com/csharp-json.html
https://json2csharp.com/ (先程のjsonデータクラスをjsonデータをもとに作ってくれるサイト、めっちゃ便利)
Thank you!!!!!

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

Firestoreから特定のフィールドの値を取得する最も簡単な方法(Angular)

はじめに

Firebase Firestoreから特定のフィールドの値を取得する(Angularfire)
:point_up:こちらの記事で書いてあるように、特定のフィールドの値を取得するのにかなり苦戦した経験がある。。。

しかし、何故か今回はすんなりと、しかもスマートなコードでできました:joy:
なんで前はこんなに苦労したんだろう???(わかる人、押してください・・)

実現したい事

https06.jpeg

こんな感じでFirestoreにデータが入ってたとして、欲しいデータはuserのフィールドであるisLockedの値!

こいつを取得するにはどうすればいいか?

環境

Angularfireがプロジェクトに入っていること

フィールドの値を取得

※必要最小限のコードです

import { AngularFireAuth } from '@angular/fire/auth';
import { AngularFirestore } from '@angular/fire/firestore';

export class SettingsPage implements OnInit {

isLocked;

constructor(
    private afAuth: AngularFireAuth,
    private db: AngularFirestore,
  ) {}

 ngOnInit() {
    this.afAuth.auth.onAuthStateChanged((user) => {
      if (user != null) {
    const userRef = this.db.collection('users').doc(user.uid);
    userRef.get().subscribe(docSnapshot => {
       // get('isLocked')でフィールド名を指定
      const locked: boolean = docSnapshot.get('isLocked');
      console.log('ロックされていますか? ' + locked);
      this.isLocked = locked;
    });
      }
    });
  }

}

これでusers/uid/の中にあるisLockedという値(ここではtrueかfalse)を取得できました!

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

【HTML】最も短い湯婆婆【Qiitaで試せます】

はじめに

@Nemesis さんの Javaで湯婆婆を実装してみる@belq さんのPerlのワンライナーで湯婆婆を実装してみるのリスペクト記事です。
上記のPerlの記事が現状最も短い湯婆婆ですがhtmlで頑張った所、もっと短くなりました。

<!doctype html>契約書だよ。そこに名前を書きな。<input id=N><button onclick='P.innerHTML=`フン。${n=N.value}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`'></button><p id=P>

多分これが一番短いと思います

契約書だよ。そこに名前を書きな。

See the Pen 湯婆婆 by T.D (@td12734) on CodePen.

解説

要件について

湯婆婆の要件は千と千尋の神隠しを見ても分からなかったのでこちらで勝手に決めました。

  • 名前を外部から入力可能
  • 入力された名前からランダムに1文字抜き出した新たな名前を作成する
    • このときに使用する乱数は完全乱数でなくても良い
    • 例外処理の実装は不要
  • 最終的に 契約書だよ。そこに名前を書きな。フン。(名前)というのかい。贅沢な名だねぇ。今からお前の名前は(新しい名前)だ。いいかい、(新しい名前)だよ。分かったら返事をするんだ、(新しい名前)!! の文字を画面に表示する。
    • 改行は任意
    • 上記の文字全てが1つの画面に表示されるべき(だと思われる)

使用テクニック

タグの省略

bodyタグやpタグは閉じタグが無くても動きます。
また、bodyタグの開始タグも特定の状況を除いて省略できます。
buttonタグも後ろに何もなければタグを省略できます。

テンプレートリテラルの使用

+で文字列結合するよりテンプレートリテラルを使う方が非常に短いです。

getElementByIdの省略

getElementByIdで取得したい要素のidと同じ名前の変数を定義していない場合、そのidの名前の変数に取得したい要素が代入されているのでそれを使用します。

変数宣言の簡略化

まず、varを消します。
また、変数の値を最初に使用するタイミングで変数に値を代入します。(${n=N.value}など)

新しい名前の取得

一番工夫した所です。
普通に実装した場合は以下のようになると思います。

m=n.charAt(Math.floor(Math.random()*n.length))

まず、charAtはリストの要素の取得時のように [number] で代用可能です。
次に乱数ですが、 new Date() 、最後のカッコは省略可能なので new Dateを人間が使う場合は乱数として使え、Math.random()より短いのでこちらを使いたいです。
その場合、new Dateは巨大な自然数ですのでnew Dateをn.lengthで割った余りは0から(n.length-1)になります。
また、Math.floorも不要になります。

よって、以下のように新しい名前を取得する処理を大幅に短縮できました。

m=n.charAt(Math.floor(Math.random()*n.length)) //旧、46文字
m=n[new Date%n.length] //新、22文字

おまけ

<!doctype html>について

筆者の環境では省略しても動作しますが、省略したら動かない環境も多いと思われるので今回は消さずに書きました。

湯婆婆の台詞を一度に画面に表示しなくても良い場合

buttonタグの閉じタグなどを省略できるので微妙に短くなります。

<!doctype html><p id=P>契約書だよ。そこに名前を書きな。<input id=N><button onclick='P.innerHTML=`フン。${n=N.value}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`'>

多分これが実際は一番短いと思います

契約書だよ。そこに名前を書きな。

See the Pen 湯婆婆min by T.D (@td12734) on CodePen.

外部から入力できなくても良い場合

n=>`フン。${n}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`

たった94文字です。
関数を変数に代入しても96文字で100文字切ってます。
ですが、外部から名前を入力できそうにないので今回はこちらに移動させました。
環境を構築してやればコマンドプロンプトやターミナルから引数を与えて実行できそうな気はします。
なので、

多分これが呼び出せれば一番短いと思います

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

【HTML】最も短い湯婆婆【記事内で試せます】

はじめに

@Nemesis さんの Javaで湯婆婆を実装してみる@belq さんのPerlのワンライナーで湯婆婆を実装してみるのリスペクト記事です。
上記のPerlの記事が現状最も短い湯婆婆ですがhtmlで頑張った所、もっと短くなりました。

<!doctype html>契約書だよ。そこに名前を書きな。<input id=N><button onclick='P.innerHTML=`フン。${n=N.value}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`'></button><p id=P>

多分これが一番短いと思います

契約書だよ。そこに名前を書きな。

See the Pen 湯婆婆 by T.D (@td12734) on CodePen.

解説

要件について

湯婆婆の要件は千と千尋の神隠しを見ても分からなかったのでこちらで勝手に決めました。

  • 名前を外部から入力可能
  • 入力された名前からランダムに1文字抜き出した新たな名前を作成する
    • このときに使用する乱数は完全乱数でなくても良い
    • 例外処理の実装は不要
  • 最終的に 契約書だよ。そこに名前を書きな。フン。(名前)というのかい。贅沢な名だねぇ。今からお前の名前は(新しい名前)だ。いいかい、(新しい名前)だよ。分かったら返事をするんだ、(新しい名前)!! の文字を画面に表示する。
    • 改行は任意
    • 上記の文字全てが1つの画面に表示されるべき(だと思われる)

使用テクニック

タグの省略

bodyタグやpタグは閉じタグが無くても動きます。
また、bodyタグの開始タグも特定の状況を除いて省略できます。
buttonタグも後ろに何もなければタグを省略できます。

テンプレートリテラルの使用

+で文字列結合するよりテンプレートリテラルを使う方が非常に短いです。

getElementByIdの省略

getElementByIdで取得したい要素のidと同じ名前の変数を定義していない場合、そのidの名前の変数に取得したい要素が代入されているのでそれを使用します。

変数宣言の簡略化

まず、varを消します。
また、変数の値を最初に使用するタイミングで変数に値を代入します。(${n=N.value}など)

新しい名前の取得

一番工夫した所です。
普通に実装した場合は以下のようになると思います。

m=n.charAt(Math.floor(Math.random()*n.length))

まず、charAtはリストの要素の取得時のように [number] で代用可能です。
次に乱数ですが、 new Date() 、最後のカッコは省略可能なので new Dateを人間が使う場合は乱数として使え、Math.random()より短いのでこちらを使いたいです。
その場合、new Dateは巨大な自然数ですのでnew Dateをn.lengthで割った余りは0から(n.length-1)になります。
また、Math.floorも不要になります。

よって、以下のように新しい名前を取得する処理を大幅に短縮できました。

m=n.charAt(Math.floor(Math.random()*n.length)) //旧、46文字
m=n[new Date%n.length] //新、22文字

おまけ

<!doctype html>について

筆者の環境では省略しても動作しますが、省略したら動かない環境も多いと思われるので今回は消さずに書きました。

湯婆婆の台詞を一度に画面に表示しなくても良い場合

buttonタグの閉じタグなどを省略できるので微妙に短くなります。

<!doctype html><p id=P>契約書だよ。そこに名前を書きな。<input id=N><button onclick='P.innerHTML=`フン。${n=N.value}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`'>

多分これが実際は一番短いと思います

契約書だよ。そこに名前を書きな。

See the Pen 湯婆婆min by T.D (@td12734) on CodePen.

外部から入力できなくても良い場合

n=>`フン。${n}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`

たった94文字です。
関数を変数に代入しても96文字で100文字切ってます。
ですが、外部から名前を入力できそうにないので今回はこちらに移動させました。
環境を構築してやればコマンドプロンプトやターミナルから引数を与えて実行できそうな気はします。
なので、

多分これが呼び出せれば一番短いと思います

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

HTMLで最も短い湯婆婆(記事内で試せます)

はじめに

@Nemesis さんの Javaで湯婆婆を実装してみる@belq さんのPerlのワンライナーで湯婆婆を実装してみるのリスペクト記事です。
上記のPerlはワンライナーの湯婆婆ですがhtmlで頑張った所、もっと短くなりました。

<!doctype html>契約書だよ。そこに名前を書きな。<input id=N><button onclick='P.innerHTML=`フン。${n=N.value}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`'></button><p id=P>

多分これが一番短いと思います

追記
コメントで指摘して頂きましたが、以下の記事にあるコードを詰めるとRubyで無慈悲に最短記録が更新されるそうです。(悲しみ)
Rubyで湯婆婆を実装してみる

まだ記事にはなっていないので、
記事になっている湯婆婆では多分これが一番短いと思います

契約書だよ。そこに名前を書きな。

See the Pen 湯婆婆 by T.D (@td12734) on CodePen.

上から記事内でも湯婆婆を試せます。
※コピー用:荻野千尋

解説

要件について

湯婆婆の要件は千と千尋の神隠しを見ても分からなかったのでこちらで勝手に決めました。

  • 名前を外部から入力可能
  • 入力された名前からランダムに1文字抜き出した新たな名前を作成する
    • このときに使用する乱数は完全乱数でなくても良い
    • 例外処理の実装は不要
  • 最終的に 契約書だよ。そこに名前を書きな。フン。(名前)というのかい。贅沢な名だねぇ。今からお前の名前は(新しい名前)だ。いいかい、(新しい名前)だよ。分かったら返事をするんだ、(新しい名前)!! の文字を画面に表示する。
    • 改行は任意
    • 上記の文字全てが1つの画面に表示されるべき(だと思われる)

使用テクニック

タグの省略

bodyタグやpタグは閉じタグが無くても動きます。
また、bodyタグの開始タグも特定の状況を除いて省略できます。
buttonタグも後ろに何もなければタグを省略できます。

テンプレートリテラルの使用

+で文字列結合するよりテンプレートリテラルを使う方が非常に短いです。

getElementByIdの省略

getElementByIdで取得したい要素のidと同じ名前の変数を定義していない場合、そのidの名前の変数に取得したい要素が代入されているのでそれを使用します。

変数宣言の簡略化

まず、varを消します。
また、変数の値を最初に使用するタイミングで変数に値を代入します。(${n=N.value}など)

新しい名前の取得

一番工夫した所です。
普通に実装した場合は以下のようになると思います。

m=n.charAt(Math.floor(Math.random()*n.length))

まず、charAtはリストの要素の取得時のように [number] で代用可能です。
次に乱数ですが、 new Date() 、最後のカッコは省略可能なので new Dateを人間が使う場合は乱数として使え、Math.random()より短いのでこちらを使いたいです。
その場合、new Dateは巨大な自然数ですのでnew Dateをn.lengthで割った余りは0から(n.length-1)になります。
また、Math.floorも不要になります。

よって、以下のように新しい名前を取得する処理を大幅に短縮できました。

m=n.charAt(Math.floor(Math.random()*n.length)) //旧、46文字
m=n[new Date%n.length] //新、22文字

おまけ

<!doctype html>について

筆者の環境では省略しても動作しますが、省略したら動かない環境も多いと思われるので今回は消さずに書きました。

湯婆婆の台詞を一度に画面に表示しなくても良い場合

buttonタグの閉じタグなどを省略できるので微妙に短くなります。

追記
@vf8974 さんの指摘で更に短くなりました。

<!doctype html>契約書だよ。そこに名前を書きな。<input id=N><button onclick='body.innerHTML=`フン。${n=N.value}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`'>

契約書だよ。そこに名前を書きな。

See the Pen 湯婆婆min by T.D (@td12734) on CodePen.

多分これが実際は一番短いと思います

外部から入力できなくても良い場合

n=>`フン。${n}というのかい。贅沢な名だねぇ。今からお前の名前は${m=n[new Date%n.length]}だ。いいかい、${m}だよ。分かったら返事をするんだ、${m}!!`

たった94文字です。
関数を変数に代入しても96文字で100文字切ってます。
ですが、外部から名前を入力できそうにないので今回はこちらに移動させました。
環境を構築してやればコマンドプロンプトやターミナルから引数を与えて実行できそうな気はします。

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

Styled Componentを使ってアコーディオンを実装【react.js, typescript, javascript, styled-compoent】

TODO

Styled-Componentを使ってアコーディオンアニメーションをさくっと実装

CODE

index.ts
const Accordion = styled.div`
    max-height: 0;
    overflow: hidden;
    max-height: ${props => (props.isHidden ? '0' : '500px')};
    transition: max-height 1s;
`
index.ts
function ComponentName({isHidden, children}: Props) {
     return (
       <>
         <Accordion isHidden={isHidden}>
              {children}
         </Accordion>
      </>
    )
}

REFERENCE

https://styled-components.com/

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

Styled Componentを使ってアコーディオンを実装する方法【react.js, typescript, javascript, styled-compoent】

TODO

Styled-Componentを使ってアコーディオンアニメーションをさくっと実装
https://styled-components.com/

CODE

index.ts
const Accordion = styled.div`
    max-height: 0;
    overflow: hidden;
    max-height: ${props => (props.isHidden ? '0' : '500px')};
    transition: max-height 1s;
`
index.ts
function ComponentName({isHidden, children}: Props) {
     return (
       <>
         <div style={{ overflow: 'hidden' }}>
           <Accordion isHidden={isHidden}>
              {children}
           </Accordion>
         </div>
      </>
    )
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのフラッシュを他のコンテンツを動かさずにフェードアウトさせる

概要

Railsにてエラー等が発生した時、フラッシュを使用し、ページ上部にメッセージを表示させ方法はよく行うことだと思います。
基本的にページを再読み込みするか、別のページへ移動するかしない限り、フラッシュは上部に表示されたままです。
この表示をページを変えなくてもフェードアウトするようプログラムする。

方向性

基本的なflashの設定を行いviewに配置する。class属性を指定し、javascriptでタイマー設定をし、新たなclassを追加し、cssを読み込ませます。

ちなみにjQueryにfadeout()というメソッドがあり、そちらを使うこともできる(記述も簡単である)。
しかし、フェードアウトした後、表示されていたflashの範囲分、ページが上部に急激に上がるため、ユーザー側の立場を考えると困ると考えました。(例:クリックしようとした瞬間、または記事を読んでいる時ページが急激に動く等。)
勉強の意味も込めて、違った方法でフェードアウトさせてみました。

事前準備

controllerでのflashの設定、javascriptの設定等は行われている前提とします。

1.該当項目にclass属性を設定

~html.erb
<body>
  <% if flash.notice %>
    <p class="fadeoutTarget"><%= flash.notice %></p>
  <% end %>
</body>

flash.noticeを囲うpタグにclass属性で"fadeoutTarget"と設定します。もちろん名前は任意で設定可能です。

2.javascriptの設定

app/assets/javascripts/script.js
$(document).on(turbolinks:load, function() {
  /* fadeoutTargetというclassを持った要素を選択 */
  const fadeoutElement = document.querySelector(".fadeoutTarget");
  /* 一定時間経過したらfadeoutというclassを追加する */
  function flashFadeout() {
    setTimeout(function() {
      fadeoutElement.classList.add("fadeout");
    }, 5000);
  }
  /* fadeoutTargetという要素を持ったdocumentがあれば実行 */
  if (fadeoutElement != null) {
    window.onload = flashFadeout();
  }

.querySelector()で任意のclass属性を持った要素を選択します。
この場合、pタグ内のflashの要素を取り出すように設定しています。
setTimeout()で一定時間後、関数を実行するというプログラムになります。
数字の箇所は任意で設定できます。
最後のif文はなくても動くのですが、処理上はfadeoutElementがない時(flash:noticeがない時)、"fadeoutElementがないですよ!"というエラーが出てしまうため、このようにif文で囲っております。

3.cssの設定

app/assets/stylesheets/application.scss
p.fadeoutTarget.fadeout {
  opacity: 0;
  transition: 1s;
}

前項のjavascriptが機能した時、cssが適用されフェードアウトされます。
厳密にはその場で透明になるため、フェードアウト後ページが急激に動いたりすることはありません。

まとめ

要素をフェードアウトさせる方法はたくさんありますので、その都度必要な方法で処理していけるようにしていきたいです。
今回の場合、ページの急激なスクロール移動はありませんが、上部にflash分の余白ができてしまうため、少し見栄えが悪いかもしれません。

個人的には、納得した形で収まったと考えております。またjavascriptの勉強にもなり良い機会でした。

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

【HTML&JavaScript】canvasでアニメーションを作る方法

プログラミング勉強日記

2020年11月13日
10月の記事でHTMLでcanvasを使うと図形を描ける方法を扱ったが、JavaScriptと組み合わせることでアニメーションを作れると知ったのでその方法をまとめる。

アニメーションを作成する方法

 Canvasはまず、HTML内にCanvasタグを用いてアニメーションを描画する範囲を指定する必要がある。タグを指定して、JavaScriptで動的な動きをかく。

<!--idがCanvasの100×100の大きさのCanvasを作成する -->
<canvas id="Canvas" width="100" height="100"></canvas>

サンプルコード

HTML
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script type="text/javascript" src="sample.js"></script>
    <style>
      #canvas{
        background:#999;
      }
    </style>
  </head>
  <body>
    <!-- Canvasタグでキャンバスを描写する -->
    <canvas id="canvas" width="100" height="100"></canvas>
  </body>
</html>
JavaScript
var can = document.getElementById("canvas");
var ctx = can.getContext("2d");

//アニメーションカウンター
var count = 0;
var timer = setInterval(function(){
  ctx.fillStyl = "#fff"; // 消去時の色
  ctx.clearRect(0,0,300,300);  // 消す
  ctx.fillStyle = "#f00"; // 塗りつぶし色を赤に
  ctx.fillRect(30 + count, 30 + count, 30, 30);
  count++;
  if(count > 200){
    clearInterval(timer);
  }
},100);

参考文献

Canvasを覚える!HTMLでアニメーションを作成する方法【初心者向け】

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

【解決】Rails6 + jQuery | Uncaught ReferenceError: $ is not defined

Rails6でjqueryを使って個人開発中Uncaught ReferenceError: $ is not definedとか言うエラーが出た。色んな記事で書いてあったrequire("jquery")とかwindow.$ = $;を試してみたがダメ。結果的に海外の変な記事を参考に解決できたのでメモ。

スクリーンショット 2020-11-13 14.40.01.png

前提

  • Railsのバージョンが
    • Railsバージョン5は対象外
  • JQueryは導入済み

結論

app/javascript/packs/application.jsに以下を追加

app/javascript/packs/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

// ここから追加
var jQuery = require('jquery')
global.$ = global.jQuery = jQuery;
window.$ = window.jQuery = jQuery;
// ここまで

参考にしたサイト
https://qiita.com/takuma_0625/items/adfee3a2305500c6d3a8
https://rubyyagi.com/how-to-use-bootstrap-and-jquery-in-rails-6-with-webpacker/

Rails
jQuery

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

【あえてjavascriptのみでtodoリストを作ってみた】

Vue.jsでtodolistを作るのはなんとなくイメージできるが、生javascriptで作るにはどうするのか気になったので作ってみることにした。

sessionにはカテゴリやタスクを追加するたびに追加するようにしたかったがそれだと、index番号が保管されず、順番がばらばらになるので毎回ページ全体ごと保存することにした。

1,まずはカテゴリを横に永遠つくれるようにする。
2,各カテゴリにタスク追加ボタン&formを設置
3,そのボタンがおされたら、formの一個うえのDOMを指定してそこにタスクを作る。
4,カテゴリの追加やタスクの追加があるたびにページ全体ごとsessionに保存
5,ページをreloadするたびsession情報を取得

1ED62026-EEED-4ED8-8032-9DABE956A9A2_4_5005_c.jpeg

session取得部分

const htmls = JSON.parse(localStorage.getItem(0));
if (htmls) {
    lists.innerHTML += htmls;
        // 配列として保存しているため「,」がそのまま表示されてしまうためここで消している。
    lists.innerHTML = lists.innerHTML.replace(/,/g, "");
}

カテゴリ追加部分

const createList = (listname) => {
    const lists = document.getElementById("lists");
    const html = `<div class="list-range">
                        <p id="keyname" class="text-center listtitle">${listname}</p>
                        <a id="delete" class="delete-list">×</a>
                        <div class="tasks"></div>
                        <p id="addCard" class="addcard">+ カードを追加</p>
                        <form id="taskForm" class="task-form" autocomplete="off" action="javascript:void(0)">
                            <input type="text" id="listname">
                            <input type="submit" value="タスクを追加">
                        </form>
                    </div>`;
    lists.innerHTML += html;
    const arrayLists = [];
    // localstarageからデータの取得&デコード
    const items = JSON.parse(localStorage.getItem(0));
    // arrayListsにitemsを配列として設置
    if(items){
        arrayLists.push(items);
    }
    arrayLists.push(html);

    deleteTaskFromLocalStorage();
    saveTaskToLocalStorage(JSON.stringify(arrayLists)); 
}

JSON.parse JSON文字列をjavascriptのオブジェクトや値にデコードする

.push() 配列の一番うしろに値を追加

JSON.stringify JavaScript のオブジェクトや値を JSON 文字列に変換

タスク追加部分

$(document).on('submit', '#taskForm', e => {
    const task = e.target.listname.value;
    e.target.style.display = "none";
    e.target.previousElementSibling.style.display = "block";
    if (task !== ""){
        e.target.previousElementSibling.previousElementSibling.innerHTML += `<p class="task">${task}</p>`;
    }

    deleteTaskFromLocalStorage();
    saveTaskToLocalStorage(JSON.stringify(lists.innerHTML)); 
});

e.target イベントを起こしたHTML
previousElementSibling 一個前のDOM要素取得

todolist全体のcode
https://github.com/KEN3pei/todolist

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

【JS】importとexport defaultの使い方。別ファイル間でのデータの受け渡し方法

vue.jsで変数やメソッドを外部のjsファイルに切り出したときの、import fromとexport defaultの使い方がいまいちわかっていなかったので確認。

目次

  1. import A from B
  2. export default
  3. 複数の変数を外部に渡す場合(export)
  4. インポート時に名前を変更する方法

import A from B

import 変数名 from "ファイルパス"

importの後に記述する変数名は任意。export defaultで設定している名前以外でも問題ない。

ファイルパスの拡張子(.js)は不要。



▼Vueテンプレート読み込みの場合
vueのテンプレートを読み込む場合は、importのあとは任意の名称をつけることができる。

import 任意の名前 from "テンプレートのファイルパス"

.vue
import 任意の名前 from "テンプレートのファイルパス"

export default {
  components: {
     指定した名前
  },

設定した任意の名前はcomponents:に記述する必要がある。
(jsファイルから読み込んだ変数やメソッドは記述不要。)

ファイルパスの拡張子(.vue)は不要。


export default

export defaultは外部に渡す変数を1つ指定できる。

※注: 渡せる変数は1つのみ。複数渡したい場合はexportを使う。

変数エクスポートの例

.js
const a = 100
const b = 1.10
const price = a * b

export default price


複数の変数を外部に渡す場合(export)

複数の変数を渡したい場合は、defaultを外しexportとする。

▼実例

outside.js(渡す側)
const a = 100
const b = 1.10
const price = a * b

const text = "this is js-file"

const array =[
   x: "xxx",
   y: "yyy",
   z: "zzz",
]

export {price, text, array}
受け取り側
import {price, text, array} from "./outside.js"

exportとimportの後ろは{ }で囲む。

※注: このとき、受け取り側の変数名はexportで指定した変数名と同じ出なければならない(*export defaultと異なる)

受け取ったデータは.jsファイル内またはscript内で使える値となる。

vue.jsのデータとして使いたい場合は、data:{text: text}として、vue側の変数に受け取った値を代入する必要がある。


インポート時に名前を変更する方法

importでプロパティを受け取った際に、名称を変えることも可能。

▼export defaultの場合
export defaultの場合は、importで指定する変数は任意。(export defaultで指定できる変数も一つのみ)

▼exportの場合
exportの場合は、asを使うことでimport時に名前を変更できる。

変更前 as 変更後

.js
import {text as msg, number as x} from "./outside.js"
.outside.js
const text = "this is outside.js" 
const number = 111

export {text, number}

上記のようにすることで、変数をmsgとxとしてインポートできる。

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

【JavaScript ~カウントダウンタイマーの作成~】勉強メモ13

JavaScriptちゃんと学習中。
今回はDateオブジェクトを使ってカウントダウンタイマーの作成をするについてです。
ほぼ自分の勉強メモです。
過度な期待はしないでください。

手順

1、設定した未来時刻をパラメータで受け取る
2、ゴール時間から現在時刻を引く
3、計算結果を返す

未来時刻を設定

 -Dateオブジェクトが持つ値に対して、日付と時刻の値をそれぞれ設定するメソッドが用意されている。
 それが以下のメソッド

メソッド 説明
setFullYear( )      年の値を設定する    
setMonth( )  月の値を設定する
setDatet( )  日の値を設定する
setHours( )  時の値を設定する
setMinutes( )  分の値を設定する
setSeconds( )  秒の値を設定する

 -Dateオブジェクトのメソッド一覧はこちらを参照
 MDN web docs – Date

  • Dateオブジェクトで時間を設定

 setHours、setMinutes、setSecondsメソッドで、時、分、秒をそれぞれ設定する必要があるが、
 先ず、Dateオブジェクトを使いたい場合、最初にインスタンスの生成する必要がある事に注意。
 インスタンスの生成をした事で、Dateオブジェクトを使い日時の出力や計算が出来るようになる。
 なぜなら、その計算の基準日となっているのが現在日時だからです。
 なので、インスタンス化し現在の日時を記憶したDateオブジェクトを使い日時の出力や計算が出来るようになる。

JavaScript
'use strict';
// newはオブジェクトをインスタンス化させる為のもの
// 現在の時刻(基準値)を変数goalに代入
let goal = new Date();
// メソッド使用して、23:59:59と設定
goal.setHours(23);
goal.setMinutes(59);
goal.setSeconds(59);

// ここで変数goalの中見を確認
console.log(goal);
出力結果
// 例えば今日が2020年11月13日なら
// 時、分、秒以外設定されていないので現在の年月日が取得される
Fri Nov 13 23:59:59


関数を定義

  • 設定した時刻をパラメータで受け取る関数を定義

 -残り時間を算出する為の関数を記載
 関数の定義は、functionを使用して行う。また、引数を使用して、関数に追加情報を与える。

書き方
function 関数名(引数名){
  //まとめたい処理内容
 console.log(`私の名前は${引数名}`です);
};

// 引数を受け取る関数を呼び出す
定数名(値) → その値は関数の引数に代入される

 -パラメータとして時間が設定されたDateオブジェクトを引数として受け取る関数を定義する
 

JavaScript
'use strict';

// 関数countdownを定義
// 引数にsetを追加して値を受け取られるようにする(引数名はどんな名前でも可能)
function countdown(set) {
  //まとめたい処理内容
}

let goal = new Date();
goal.setHours(23);
goal.setMinutes(59);
goal.setSeconds(59);

// 定数counterに、countdown関数の処理を代入
// 設定した時刻が代入されている変数goalの値を関数の引数として渡す
const counter = countdown(goal);


  • 残り時間を算出する為のプログラムを記載

 -countdown関数の中身の処理を記載
 残り時間を算出する為には、設定した時刻から現在の時刻を引くという計算をする。
 その為には、先ずは現在の時刻を記憶したDateオブジェクトを生成する、つまりインスタンスの生成をする。

JavaScript
function countdown(set) {
  // 現在の時刻(基準値)を変数nowに代入
  const now = new Date();
}



 -残り時間を算出する計算式を記載
 先ず、設定した時刻から現在の時刻を引く為に、getTime( )メソッドを使用。
 getTime( )メソッドは、1970年1月1日0時00分から、そのオブジェクトの基準日になっている
 日時迄に経過したミリ秒を取得するメソッド。
 例えば、現在2020年11月13日午後12時00分だと、getTime( )メソッドで1605279599309ミリ秒で出る。
 これを設定した時刻のミリ秒から引くという計算をする。

JavaScript
function countdown(set) {
  const now = new Date();
  // 設定した時刻をが入っている引数と現在の時刻を記憶しているnowを使用して
  // getTime( )メソッドでミリ秒で残り時間を算出し、定数restに代入
  const rest = set.getTime() - now.getTime();
}


 -定数restのミリ秒をもとに、秒、分、時、日を計算
 
 先ずは秒から。
 元の数値がミリ秒なので、1000で割れば全体の秒数が出る。
 さらに、その答えを60で割れば分が出ます。すなわちの割った余りが秒になる。

 例:39455000÷1000÷60=657 余り35
 ここでいうと35が秒数

 これをコードで書くと下記のようになる。
 

JavaScript
function countdown(set) {
  const now = new Date();
  const rest = set.getTime() - now.getTime();

  const sec = Math.floor(rest / 1000) % 60;
}

 1000で割った小数点以下は秒に満たないものなのでfloorメソッドで切り捨てる。
 また、プログラムにおける演算式の%は、計算した余りを表示するものなの注意(決して割り算ではない)


 次に分ですが、
 上で計算した答えの結果を60で割った答えの余りが時になる。
 657÷60=10 余り57
 ここでいうと57が分になる。

 これをコードで書くと下記のようになる。
 

JavaScript
function countdown(set) {
  const now = new Date();
  const rest = set.getTime() - now.getTime();
  const sec = Math.floor(rest / 1000) % 60;

  const min = Math.floor(rest / 1000 / 60) % 60;
}

 ここも1000で割った小数点以下は分に満たないものなのでfloorメソッドで切り捨てる。
 また、プログラムにおける演算式の%は、計算した余りを表示するものなの注意(決して割り算ではない)

 次に時です。
 
 上で計算した答えの結果を24で割った答えの余りが時になる。
 10÷24=0 余り10
 ここでいうと10が時になる。

 これをコードで書くと下記のようになる。
 

JavaScript
function countdown(set) {
  const now = new Date();
  const rest = set.getTime() - now.getTime();
  const sec = Math.floor(rest / 1000) % 60;
  const min = Math.floor(rest / 1000 / 60) % 60;

  const hours = Math.floor(rest / 1000 / 60 / 60) % 24;
}

 ここも1000で割った小数点以下は1時間に満たないものなのでfloorメソッドで切り捨てる。
 また、プログラムにおける演算式の%は、計算した余りを表示するものなの注意(決して割り算ではない)

 最後に日ですが、
 上の計算 10÷24=0 余り10
 の余りじゃない方が日になるので、
 ここでいうと0が日になる。

 これをコードで書くと下記のようになる。
 

JavaScript
function countdown(set) {
  const now = new Date();
  const rest = set.getTime() - now.getTime();
  const sec = Math.floor(rest / 1000) % 60;
  const min = Math.floor(rest / 1000 / 60) % 60;
  const hours = Math.floor(rest / 1000 / 60 / 60) % 24;

  const days = Math.floor(rest / 1000 / 60 / 60 / 24);
}

 今後は、余りを表示させたい訳ではなく、割り算の結果を表示させたいのでこうなる。

 -こうして出揃った、秒、分、時、日を配列にして定数に代入して、呼び出し元にリターンさせる。

JavaScript
function countdown(set) {
  const now = new Date();
  const rest = set.getTime() - now.getTime();
  const sec = Math.floor(rest / 1000) % 60;
  const min = Math.floor(rest / 1000 / 60) % 60;
  const hours = Math.floor(rest / 1000 / 60 / 60) % 24;
  const days = Math.floor(rest / 1000 / 60 / 60 / 24);

  const count = [days, hours, min, sec];
  return count;
}

 「return 値」と書くことで、関数はその値を戻り値として返す。

  • 現在の完成形
JavaScript
'use strict';

function countdown(set) {
  const now = new Date();

  const rest = set.getTime() - now.getTime();
  const sec = Math.floor(rest / 1000) % 60;
  const min = Math.floor(rest / 1000 / 60) % 60;
  const hours = Math.floor(rest / 1000 / 60 / 60) % 24;
  const days = Math.floor(rest / 1000 / 60 / 60 / 24);
  const count = [days, hours, min, sec];

  return count;
}

let goal = new Date();
goal.setHours(23);
goal.setMinutes(59);
goal.setSeconds(59);

const counter = countdown(goal);


  • 保存されたデータをもとに代入

 -後は、関数の処理データが代入された定数counterをもとに、 
 テンプレートに代入させるだけ。

 テンプレート例:
 ${counter[1]}時間${counter[2]}分${counter[3]}秒

 配列の要素の取得の仕方は、配列名[配列番号]
 配列番号は、0から1、2・・・と始まることに注意

 最後に、getElementByIdメソッドを使って要素取得し、textContentプロパティを使用し出力する。

HTML
<p>後、<span id="timer"></span>で1日が終わります。</p>
JavaScript
'use strict';

function countdown(set) {
  const now = new Date();

  const rest = set.getTime() - now.getTime();
  const sec = Math.floor(rest / 1000) % 60;
  const min = Math.floor(rest / 1000 / 60) % 60;
  const hours = Math.floor(rest / 1000 / 60 / 60) % 24;
  const days = Math.floor(rest / 1000 / 60 / 60 / 24);
  const count = [days, hours, min, sec];

  return count;
}

let goal = new Date();
goal.setHours(23);
goal.setMinutes(59);
goal.setSeconds(59);

const counter = countdown(goal);
const time = `${counter[1]}時間${counter[2]}${counter[3]}`;
document.getElementById('timer').textContent = time;
出力結果
後、10時間0分48秒で1日が終わります。


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

【Vue】メソッドを外部のjsファイルに移動する方法。外部ファイルの処理結果をvueに戻す。

メソッドを外部のjsファイルに移動する方法について。

変数を外部ファイル(.js)に切り出してexport defaultでインポートする方法はこちら

今回は応用編として、.vue内のメソッドを外部ファイルに切り出す方法。

流れと考え方

操作の流れは、vueのデータをjsに渡して、処理後に戻す作業になる。

(1).vue -> (2).js -> (3).vue



(1) .vueの処理
・vueのメソッドを実行
・メソッド内で変数を定義し、jsファイルにプロパティ名でデータを渡す。

(2) .jsの処理
・vueのデータを引数としてプロパティで受け取り処理を実行後、戻り値で返す。

(3) .vueの処理
.jsから戻ってきた値を、vueの値(this.xxx)に代入する。



▼考え方
Inputする(渡す)データとOutputする(戻す)データの受け渡しを意識することが大切。

.vueの処理(概念)
methods:{
    vueのメソッド名(){
      const {受け取るデータ} = jsファイルのコマンド名({
        渡すデータ
      })

      vueのデータ = 受け取るデータ
    }
  } 
.jsの処理(概念)
const メソッド名 = ({受け取るデータ}) => {
    処理
  return {
    渡すデータ
  }
}

export default 渡すメソッド

jsファイルに引数としてデータを渡し、処理後にreturnでvue側の変数名と一致するデータを返す。

最後に、受け取ったデータをvueのデータに入れて受け取り完了となる。


実例

実例で見るとよりわかりやすい。

.vue(scriptの中身)
//外部ファイル(transformationJs.js)からメソッドをjsMultiとしてインポート
import jsMulti from "./transformationJs"

export default {
  data(){
    return{
      A: 1,
      B: 2,
      x: 100,
      y: 200,
    }
  },

  //ここが重要!jsファイルのメソッドとデータをやりとりする記述
  methods:{
    btnClicked(){
      //処理結果をいれるプロパティを用意
      const {A, B} = jsMulti({
        //引数内でプロパティ名を指定してデータを渡す
        A: this.A,   
        B: this.B,
        x: this.x,
        y: this.y
      })

      //vueのデータにjsファイルの処理結果を代入
      this.A = A
      this.B = B
    }
  }
}

読み込んだのはテンプレートではなくメソッドなので、components:{}は不要

.js
//メソッドを定義
const jsMulti = ({A, B, x, y}) => {
  //処理結果を代入する変数を定義
  const _A = A * x
  const _B = B * y

  return {  //ここが重要!
    //vue側で設定したプロパティにデータを入れる
    A: _A,
    B: _B
  }
}

//外部ファイルに渡すメソッド
export default jsMulti

・.js側で処理済みの値はわかりやすいように_を付けたが、これは別に何でもいい。(Aのままでもいいし、他の新しい変数名でも可)

・.jsのメソッドは最後にreturnをプロパティとして返す。

A = _Aはエラーになる。(※プロパティの形になっていない)

Aのみで値を入れない場合は、空のデータが返る。 (※AはA:Aの省略表記となるため、変数Aを定義していない場合は空のプロパティを返したことになる。)

・A:Aの省略表記Aをshort propertyと呼ぶ


処理結果事例とフルコード

上記の実例は、ボタンをクリックする毎に、A × 100、B × 200をする処理となる。

image.png

↓ クリック

image.png

↓ クリック

image.png

▼フルコード

.vue
<template>
  <div>
   <!-- クリックイベントでbtnClickedを発火 -->
    <button
      @click="btnClicked"
    >【Click Me】</button>

    <p>・A: {{A}}</p>
    <p>・B: {{B}}</p>

  </div>
</template>

<script>
import jsMulti from "./transformationJs"

export default {
  data(){
    return{
      A: 1,
      B: 2,
      x: 100,
      y: 200,
    }
  },
  methods:{
    btnClicked(){
      const {A, B} = jsMulti({
        A: this.A,
        B: this.B,
        x: this.x,
        y: this.y
      })

      this.A = A
      this.B = B
    }
  }
}
</script>
transformationJs.js
const jsMulti = ({A, B, x, y}) => {
  const _A = A * x
  const _B = B * y

  return {
    A: _A,
    B: _B
  }
}

export default jsMulti


配列の中のコマンドを実行する例

v-forで表示した配列として格納されているボタンに対し、外部ファイルの処理を実行する場合の例。(*流れのみ)

(1)ボタンタグをクリックするとメソッドが発火する。
このとき、引数でクリックされたボタンの要素を渡す。

@click="onClickButton(command)

(2)ボタンクリックで発火させる処理
command.onClickでJSファイル内のプロパティonClickを実行している。

.vue
<script>
import cellCommands from "./commands"

export default {
  methods:{
    onClickButton(command){

      const {
        //js内で処理後の値を格納する変数を用意(output用)
        rows,
        selectedCells,
      } = command.onClick({  //jsファイルに渡すデータ(input用)。プロパティ名: 値 。returnで返す。
        selectedCells: this.selectedCells,
        rows: this.innerValue,
        maxColNum: this.maxColNum,
        thPosition: this.thPosition,
      })

      //jsの出力をvueに渡す(output)
      this.innerValue = rows
      this.selectedCells = selectedCells
    },
  }
}
</script>

各要素のプロパティonClickの中に記述された処理が実行される。

commands.js
const commands = [
  {
    cmd: 'clearSelects',
    text: "選択解除",
    icon: 'mdi-cancel',
    isActive: anySelectedCells,
    onClick: ({
      rows,
    }) => {
      return {
        selectedCells:[],
        rows,
      }
    }
  },
  {
    cmd: 'addColumnBefore',
    text:"左に列追加",
    icon: 'mdi-table-column-plus-before',
    isActive: (params) => notThColandSelectedFirstCol(params) && singleSelectedCell(params),
    onClick: ({
      selectedCells,
      rows,
      thPosition
    }) => {

      if(selectedCells.length != 1) {
        return {
          selectedCells,
          rows,
        }
      }

      //変換後の出力の値は別の変数に代入。一時的として、_変数名とする。
      const _rows = rows.map((tr, index) => {
        let newCell
        if(thPosition == 'row' && index === 0){
          newCell = initTh()
        }else{
          newCell = initTd()
        }
        tr.article_items = [
          ...tr.article_items.slice(0, selectedCells[0].cellIndex),
          ...[newCell],
          ...tr.article_items.slice(selectedCells[0].cellIndex),
        ]
        return tr
      })

      const _selectedCells = selectedCells.map((cell)=>{
        const {cellIndex: cCellIndex} = cell
        if(cCellIndex >= selectedCells[0].cellIndex){
          return {
            ...cell,
            cellIndex: cCellIndex + 1
          }
        } else {
          return cell
        }
      })

      //vueに戻すoutput用
      return {
        selectedCells: _selectedCells,
        rows: _rows,
      }
    }
  },
  {
    cmd: 'addColumnAfter',
    text:"右に列追加",
    icon: 'mdi-table-column-plus-after',
    isActive: singleSelectedCell,
    onClick: ({
      selectedCells,
      rows,
      thPosition,
    }) => {
      if(selectedCells.length != 1) {
        return {
          selectedCells,
          rows,
        }
      }

      const _rows = rows.map((tr, index) => {
        let newCell
        if (thPosition == 'row' && index === 0) {
          newCell = initTh()
        } else {
          newCell = initTd()
        }

        return {
          ...tr,
          article_items: [
            ...tr.article_items.slice(0, selectedCells[0].cellIndex + 1),
            ...[newCell],
            ...tr.article_items.slice(selectedCells[0].cellIndex + 1),
          ]
        }
      })

      const _selectedCells = selectedCells.map((cell)=>{
        const {cellIndex: cCellIndex} = cell
        if(cCellIndex > selectedCells[0].cellIndex){
          return {
            ...cell,
            cellIndex: cCellIndex + 1
          }
        }else {
          return cell
        }
      })

      return {
        rows: _rows,
        selectedCells: _selectedCells
      }
    }
  },
  {
    cmd: 'removeColumn',
    text:"列削除",
    icon: 'mdi-table-column-remove',
    color: 'error',
    isActive: (params) => anySelectedCells(params) && notAllColumnsSelected(params),
    onClick: ({
      selectedCells,
      rows,
      maxColNum,
    }) => {
      const cellIndicesToRemove = new Set(
        selectedCells.map((cell) => cell.cellIndex)
      )

      if(cellIndicesToRemove.size == maxColNum) {
        return
      }

      const _rows = rows.map((row) => {
        return {
          ...row,
          article_items: row.article_items.filter((cells, cellIndex)=> 
            !cellIndicesToRemove.has(cellIndex)
          )
        }
      })

      return {
        rows: _rows,
        selectedCells: []
      }
    },
  },
  {
    cmd: 'addRowAfter',
    text:"下に行追加",
    icon: 'mdi-table-row-plus-after',
    isActive: singleSelectedCell,
    onClick: ({
      selectedCells,
      rows,
      maxColNum,
      thPosition,
    }) => {
      if(selectedCells.length != 1) {
        return {
          selectedCells,
          rows,
        }
      }

      const _rows = [
        ...rows.slice(0, selectedCells[0].rowIndex + 1),
        ...[getTr( {maxColNum, thPosition} )],
        ...rows.slice(selectedCells[0].rowIndex + 1),
      ]

      const _selectedCells = selectedCells.map((cell) => {
        const { rowIndex: cRowIndex } = cell
        if (cRowIndex > selectedCells[0].rowIndex){
          return {
            ...cell,
            rowIndex:  cRowIndex + 1,
          }
        } else {
          return cell
        }
      })

      return {
        rows: _rows,
        selectedCells: _selectedCells
      }
    },
  },
  {
    cmd: 'addRowBefore',
    text:"上に行追加",
    icon: 'mdi-table-row-plus-before',
    isActive: (params) => singleSelectedCell(params) && notThColandSelectedFirstRow(params),
    onClick: ({
      selectedCells,
      rows,
      maxColNum,
      thPosition,
    }) => {

      if(selectedCells.length != 1) {
        return {
          selectedCells,
          rows,
        }
      }

      const _rows = [
        ...rows.slice(0, selectedCells[0].rowIndex),
        ...[getTr( {maxColNum, thPosition} )],
        ...rows.slice(selectedCells[0].rowIndex),
      ]

      const _selectedCells = selectedCells.map((cell) => {
        const { rowIndex: cRowIndex } = cell
        if (cRowIndex >= selectedCells[0].rowIndex){
          return {
            ...cell,
            rowIndex:  cRowIndex + 1,
          }
        } else {
          return cell
        }
      })

      return {
        rows: _rows,
        selectedCells: _selectedCells
      }
    }
  },
  {
    cmd: 'removeRow',
    text:"行削除",
    icon: 'mdi-table-row-remove',
    color: 'error',
    isActive: (params) => anySelectedCells(params) && notAllRowsSelected(params),
    onClick: ({
      selectedCells,
      rows,
    }) => {
      const rowIndicesToRemove = new Set(
        selectedCells.map((cell)=> cell.rowIndex)
      )

      if(rowIndicesToRemove.size == rows.length) return

      const _rows = rows.filter((row, rowIndex)=> 
        !rowIndicesToRemove.has(rowIndex)
      )

      return {
        rows: _rows,
        selectedCells: []
      }
    },
  }
]

export default commands

commands.jsの中身はかなり長いので、vueファイルから切り出すことでコードを見やすくすることができる。


参考リンク

export defaultとimport fromの使い方
複数の変数やメソッドを渡す方法
MDN公式 export default

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

【React Native】Invariant Violation "main" has not been registered. This can happen if: *Metro (the local dev server ... の解決法

はじめに

自分の場合はとても処方的なミスでした (react-navigationが関係)
他にもいくつか試した対処法も載せてあります。

expoで開発していたら以下の様なエラーが起きた。

Invariant Violation "main" has not been registered. This can happen if: *Metro (the local dev server) 
is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.*
A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called.

環境

// app.json
   {
    "sdkVersion": "38.0.0"
   }
// package.json
  "dependencies": {
    "expo": "^38.0.8",
    "react-native": "^0.62.2",
    "react-navigation": "^4.4.3",
    "react-navigation-stack": "^2.10.0",
    "react-navigation-tabs": "^2.10.1",
   }

試した対処法

調べてみるといくつか方法があった

1つめ

  1. watchman watch-del-all
  2. node_modules & package-lock.json (もしくはyarn.lock) の削除
  3. npm install (もしくはyarn install)
  4. expo install react-native-safe-area-context
  5. expo start

試してみたが画面は変わらず。。

2つめ

  1. expoを止めている状態で、ps aux | grep react-native を実行して動いているプロセスを探す
  2. 結果が表示されたら、そのプロセスを sudo kill 'pid'

自分は動いてるプロセスは見つけたものの、killすると
kill:'pid': No such process
と出て解決には至らず。。

3つめ

package.jsonのnameとApp.jsonのexpoのnameを同じにする

→ 何も変わらず

自分の解決策

結局めちゃくちゃ初歩的なミスでした。。
react-navigationを使っている部分で、 createAppContainerを呼んでいるが、肝心のimportをし忘れていた為、エラーが起こっていた様です。

// ここを書き忘れていた
// import { createAppContainer } from "react-navigation";

 const AppContainer = createAppContainer(HomeTab);

普段こんな場合は
ReferenceError: Can't find variable: Button
とかでエラーが出ると思うのですが。。。

importの部分を書き足すと正常に動作しました。

ルーティング周りで変更を加えた場合は、そこの箇所を戻してみると治るかもしれません。

メモ

expo start --no-dev --minifyで起動すると解決する」
とexpoのForumsにあったので、試してみると確かに一時的に解決した。
Forums

しかしこれはproduction用にアプリをminifyして不要な部分を削っている為、部分的に解決しただけだと思われる。
expo -Development and Production Mode

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

子供用の学習アプリケーションを作る(1)

はじめに

昨日、筋萎縮性側索硬化症(ALS)と呼ばれる病気についての記事を見かけ、以前より知っている病気ではあったもののなぜ、この病気になってしまうのかについては、知らなかった。
ネットで調べてみたところ、様々なサイトで解説されているものの、言葉が難しかったり、文字数が多く読むのが大変であった。

そこで、そのような知識を分かりやすく、まとめ尚且つ大人から子供まで使えるような学習アプリケーションを自作してみました。

実装

まず、環境構築については、下記の記事を見ていただければと思います。
参考: React Nativeをはじめる(環境構築)

ディレクトリ構成

使用したUI component, ライブラリ, フリーイラスト

UI component
1.React-native-elements
React-Native-Elementsは、フリーのクロスプラットフォームのUIcomponentです。
色々な開発者が作成したcomponentが集められ、クオリティもかなり高いです。

2.React-native-paper
React-native-paperは、使いやすく設計されているUIcomponentです。
componentは、Googleのマテリアル基準に準拠しているのでデザイン性がかなり高いという感想です。

ライブラリ
1.React Navigation
React Navigationは、画面遷移のライブラリです。
なぜ、このライブラリが必要かというと、Reactでは画面遷移のための機能が用意されていないからです。

React Navigationでは3つの方法で画面遷移ができます。

1.stack:スライドしながら画面遷移する方法
2.tabs:タブを選択することで画面遷移する方法
3.drawer:画面端から現れるメニューで画面遷移する方法

今回は、1の方法で実装を行いました。

フリーイラスト
1.manypixels
manypixelsは、フラットなイラストをフリーでdlできます。

App.js

このファイルではメインとなるページの処理を記載しています。

App.js
import 'react-native-gesture-handler';
import * as React from 'react';
import {Image, SafeAreaView, ScrollView, StyleSheet} from 'react-native';
import {Text, Card, Button} from 'react-native-elements';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import AppHeader from './components/header';
import contentsSelect from './components/contentsSelect';

const HomeScreen = ({navigation}) => {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
        <AppHeader />
        <Text h2 style={styles.hearderMessage}>
          For explorer who want to know the unknown
        </Text>
        <Text style={styles.hearderMessageBody}>
          ilearnはさまざまなトピックスをまとめ{'\n'}発信する場所だよ
        </Text>

        <Card>
          <Card.Title>探検をはじめよう</Card.Title>
          <Card.Divider />
          <Image
            source={require('../public/img/teach.png')}
            style={styles.cardImage}
          />
          <Text style={styles.cardText}>
            医学,科学,数学,コンピュータサイエンス,宇宙,{'\n'}歴史をまなぼう
          </Text>

          <Button
            style={styles.cardButtonText}
            title="出発する?"
            onPress={() => navigation.navigate('Contents')}
          />
        </Card>
      </ScrollView>
    </SafeAreaView>
  );
};

const Stack = createStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Contents" component={contentsSelect} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  headerImg: {
    width: 90,
    height: 30,
    top: 60,
  },
  hearderMessage: {
    marginTop: 100,
    textAlign: 'center',
  },
  hearderMessageBody: {
    flex: 1,
    top: 20,
    marginBottom: 60,
    textAlign: 'center',
    fontWeight: 'bold',
    color: '#93a5b1',
  },
  cardImage: {
    width: 350,
    height: 350,
  },
  cardText: {
    marginBottom: 10,
  },
  cardButtonText: {
    marginTop: 20,
    marginBottom: 20,
  },
});

export default App;

header component

名前から察するようにヘッダーについてまとめたファイルになります。

header.js
import React from 'react';
import {View, StyleSheet, Image} from 'react-native';

const AppHeader = () => {
  return (
    <View>
      <Image
        source={require('../../public/img/Header.png')}
        style={styles.headerImg}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  headerImg: {
    width: 90,
    height: 30,
    marginLeft: 20,
    top: 30,
  },
});

export default AppHeader;

contents component

こちらのcontents componentは、ユーザが実際に何のコンテンツについて学びたいかを選ばせるページの処理をまとめています。

contentsSelect.js
import React from 'react';
import {SafeAreaView, ScrollView, StyleSheet} from 'react-native';
import {Card, Title} from 'react-native-paper';
import AppHeader from './header';

const contentsSelect = () => {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView>
        <AppHeader />
        <Card style={styles.cardPadding}>
          <Card.Content>
            <Title style={styles.cardTitle}>宇宙って</Title>
            <Card.Cover
              source={require('../../public/img/alien.png')}
              style={styles.cardImg}
            />
          </Card.Content>
        </Card>
        <Card style={styles.cardPadding}>
          <Card.Content>
            <Title style={styles.cardTitle}>ALSって知ってる</Title>
            <Card.Cover
              source={require('../../public/img/health.png')}
              style={styles.cardImg}
            />
          </Card.Content>
        </Card>
        <Card style={styles.cardPadding}>
          <Card.Content>
            <Title style={styles.cardTitle}>Falcon 9がすごい</Title>
            <Card.Cover
              source={require('../../public/img/startup_isometric.png')}
              style={styles.cardImg}
            />
          </Card.Content>
        </Card>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  cardImg: {
    height: 300,
  },
  cardPadding: {
    marginBottom: 20,
  },
  cardTitle: {
    fontWeight: 'bold',
  },
});

export default contentsSelect;

動作

ezgif.com-gif-maker.gif

次回

次回は、コンテンツ選択した後の詳細画面を作り込んでいこうと思います。

おわり

かなり気合を入れて作成を始めたので、随時記事も更新していこうと思います。
また、React-vativeをさわってみたい方に対し、開発コードがお役に立てば幸いです。

参考

React-native
React-native-elements
React Navigation
React-native-paper
manypixels
難病情報センター: ALS

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

神経衰弱の制作

神経衰弱の制作

1〜13までの数字を4つ使った神経衰弱ゲームです。
まずはbody要素内のtable要素にid属性を付与します。

<body onload="init()">
  <table id="table"></table>
</body>

次にグローバル変数に配列cardsとpreCard,flipTimerを定義します。
後で制作したカードをシャッフルできるように、Arrayオブジェクトにprototypeプロパティでshuffle関数を定義します。この関数は配列の一番後ろの要素とランダムに取得した要素の値を交換していく関数です。(一番後ろの要素が繰り返すごとに1ずつ減っていくので全てのカードをシャッフルできる)

  <script>
    let cards = [], prevCard, flipTimer;

    Array.prototype.shuffle = function() {
      for(let i = this.length - 1; i >= 0; i--) {
        let r = Math.floor(Math.random() * (i + 1));
        let s = this[i];
        this[i] = this[r];
        this[r] = s;
      }
      return this;
    }

init関数を定義します。

    function init() {
      for(let i = 1; i < 14; i++)
      {
        cards.push(i);
        cards.push(i);
        cards.push(i);
        cards.push(i);
      }
      cards.shuffle();

      const table = document.getElementById("table");
      for(let i = 0; i < 4; i++) {
        let tr = document.createElement("tr");
        for(let j = 0; j < 13; j++) {
          let td = document.createElement("td");
          let value = cards[i * 13 + j];
          td.value = value;
          td.className = "card";
          td.onclick = flip;
          tr.appendChild(td);
        }
        table.appendChild(tr);
      }
    }

最初の部分で1〜13までの数字を4つ配列cardsに追加します。
その後、配列cardsを先ほど定義したshuffleメソッドでシャッフルします。

    function init() {
      for(let i = 1; i < 14; i++)
      {
        cards.push(i);
        cards.push(i);
        cards.push(i);
        cards.push(i);
      }
      cards.shuffle();

シャッフルした52枚のカードを並べます。
「let value = cards[i * 13 + j]」で52枚のカードを毎回、変数valueに代入します。td要素にはクリックしたときにflip関数が発火するようにします。

      const table = document.getElementById("table");
      for(let i = 0; i < 4; i++) {
        let tr = document.createElement("tr");
        for(let j = 0; j < 13; j++) {
          let td = document.createElement("td");
          let value = cards[i * 13 + j];
          td.value = value;
          td.className = "card";
          td.onclick = flip;
          tr.appendChild(td);
        }
        table.appendChild(tr);
      }
    }

次にflip関数を定義します。

    function flip(e) {
      let src = e.target;
      if(flipTimer || src.textContent != "") {
        return;
      }
      let value = src.value;
      src.className = "";
      src.textContent = value;
      if(prevCard == null) {
        prevCard = src;
        return;
      }

      if(prevCard.value === value) {
        prevCard = null;
        clearTimeout(flipTimer);
      } else {
        flipTimer = setTimeout(function() {
          src.className = "card";
          src.textContent = "";
          prevCard.className = "card";
          prevCard.textContent = "";
          prevCard = null;
          flipTimer = NaN;
        }, 1000);
      }
    }

クリックしたカードのデータを取得できるように、引数に「e」を指定します。
最初のif文では、変数flipTimerに値がある場合とクリックしたカードに数字の表示がある場合に処理を中断するようにしています。
次にクリックしたカードに数字の表示がない場合に自身の値を表示させて、if文で変数prevCardに最初にクリックした1枚目のカードとして値を代入します。

    function flip(e) {
      let src = e.target;
      if(flipTimer || src.textContent != "") {
        return;
      }
      let value = src.value;
      src.className = "";
      src.textContent = value;
      if(prevCard == null) {
        prevCard = src;
        return;
      }

下記ではif文で最初にクリックしたカードと2回目にクリックしたカードが同じ値かどうか判定しています。
同じでなかったときは変数flipTimerにsetTimeoutメソッドを代入します。setTimeoutメソッドは指定した時間が経過した場合に、指定した関数を発火させることができます。ここでは、1000ミリ秒(1秒)経過したら一枚目と二枚目のカードを裏返しに戻します。
一枚目と二枚目が同じかどうかに関係なく、変数prevCardの値を空にします。「flipTimer = NaN」でsetTimeoutが一度発火したら解除されるようにしています。

      if(prevCard.value === value) {
        prevCard = null;
      } else {
        flipTimer = setTimeout(function() {
          src.className = "card";
          src.textContent = "";
          prevCard.className = "card";
          prevCard.textContent = "";
          prevCard = null;
          flipTimer = NaN;
        }, 1000);
      }

下記のコードをコピーしてファイルに貼り付ければ試すことができます。
「.cardセレクタ」のbackground-imageのurl内だけ何か画像を貼り付けてください。

<!DOCTYPE html>

<html>

<head>
  <meta charset="UTF-8">
  <title>b</title>
  <style>
    td {
      width: 50px;
      height: 75px;
      font-size: 30px;
      text-align: center;
      border: 1px solid black;
      border-radius: 5px;
    }
    .card {
      background-image: url(card.png);
      background-size: 50px 75px;
    }
  </style>

  <script>
    let cards = [], prevCard, flipTimer;

    Array.prototype.shuffle = function() {
      for(let i = this.length - 1; i >= 0; i--) {
        let r = Math.floor(Math.random() * (i + 1));
        let s = this[i];
        this[i] = this[r];
        this[r] = s;
      }
      return this;
    }
    function init() {
      for(let i = 1; i < 14; i++)
      {
        cards.push(i);
        cards.push(i);
        cards.push(i);
        cards.push(i);
      }
      cards.shuffle();

      const table = document.getElementById("table");
      for(let i = 0; i < 4; i++) {
        let tr = document.createElement("tr");
        for(let j = 0; j < 13; j++) {
          let td = document.createElement("td");
          let value = cards[i * 13 + j];
          td.value = value;
          td.className = "card";
          td.onclick = flip;
          tr.appendChild(td);
        }
        table.appendChild(tr);
      }
    }

    function flip(e) {
      let src = e.target;
      if(flipTimer || src.textContent != "") {
        return;
      }
      let value = src.value;
      src.className = "";
      src.textContent = value;
      if(prevCard == null) {
        prevCard = src;
        return;
      }

      if(prevCard.value === value) {
        prevCard = null;
      } else {
        flipTimer = setTimeout(function() {
          src.className = "card";
          src.textContent = "";
          prevCard.className = "card";
          prevCard.textContent = "";
          prevCard = null;
          flipTimer = NaN;
        }, 1000);
      }
    }

  </script>
</head>

<body onload="init()">
  <table id="table"></table>
</body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

textareaを使わずに複数行文字列入力するのを作ってみた

Twitterのツイート入力するところを眺めて気がついてちょっと驚いたのでメモ程度に
作ったというのもおこがましいレベルの代物ですが…w

縦方向に勝手に伸びるのでフォントサイズとか深く気にしなくていいのは便利ですね

やり方

  • 超端的に書くとこれだけで出来ます
    • ただこのままだとめちゃくちゃ見づらい上に入力しづらいので、適当にCSS当てたほうがいいです。
<span contenteditable="true"></span>

デモ

備考

  • 入力値が HTML になるので何かしらのハンドリングをしないと扱いづらいと思います
    • 因みに文字選択状態で Ctrl + B とか、 Ctrl + I とかすると装飾がかかりますが、環境によっては多分うまく動きません
  • 気になって調べたら TinyMCE はこの方式で実装されてるみたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む