20200331のJavaScriptに関する記事は24件です。

コロナウィルス対策でリモートワークしてみたらReduxやVuexのメリットが分かった

忘れ物を取りに久々に出社したワイ

ワイ「おはようさん」

ハスケル子「おはようございます」

ワイ「なんや、ハスケル子ちゃんしかおらんのかいな」

ハスケル子「はい」
ハスケル子「みなさんリモートワークです」

ワイ「コロナウィルスのせいで基本出社禁止やもんなぁ」
ワイ「通勤せんでいいのは楽チンやけど」
ワイ「みんなに会ってバカ話ができなくて寂しいわぁ・・・」

ハスケル子「やめ太郎さん、あんまり近づかないでください」
ハスケル子「こっち向いて喋るのもやめてください」

ワイ「おお、失礼失礼」
ワイ「もしウィルス感染してたら、うつってまうもんな」

ハスケル子「いえ、単純に口が臭いんです

ワイ「Oh...」
ワイ「ごめんやで...」

リモートだと、井戸端会議が聞けなくて困る

ワイ「なんか、リモートって結構やりにくくない?」

ハスケル子「私は別に大丈夫ですね」

ワイ「そうかぁ」
ワイ「ワイはなんか、井戸端会議が聞けへんのが困るわ」

ハスケル子「井戸端会議ですか」

ワイ「せや」
ワイ「色んなとこでPMさんやら営業さんが」

  • 来月くらいに〇〇な案件が受注できそうやで!
  • □□の案件が炎上中みたいや!

ワイ「とか話してたり」
ワイ「エンジニアのみんなも」

  • この〇〇ってツールめちゃくちゃ便利やで!

ワイ「とかよく話してるやん」
ワイ「あとは」

  • あ〜、このコードなんで動かねえんだよ〜!

ワイ「ってセリフが聞こえたりとか」
ワイ「要は、その場におると何となくみんなのバイブス勝手に伝わってくるやん」
ワイ「そういう、周りの人の雑談から得る情報がけっこう大きいからさ」
ワイ「だからリモートって何かやりづらいねん」
ワイ「Slackとかによる、テキストベースのコミュニケーションが多くなってまうやん?」

ハスケル子「あ〜、確かにそうですね」
ハスケル子「でも、テキストベースのコミュニケーションが増えることのメリットもありますよ」

例えば

ハスケル子「例えば弊社は、Slackでのコミュニケーションが多いじゃないですか」

ワイ「せやな」

ハスケル子「最近なんてほぼ全員リモートワークだから」
ハスケル子「コミュニケーションのほとんどがSlackじゃないですか」

ワイ「せや」

ハスケル子「今までだったら口頭で済ませてたような軽い相談も全部Slack

ワイ「確かにな」

ハスケル子「そうすることで、みんなの会話が全部Slack上に残るのはメリットだと思います」

ワイ「あー、せやな」
ワイ「口頭で相談とかした場合には、議事録でも取らん限り記録に残らんけど」
ワイ「基本がSlackになると、いつでも簡易議事録勝手に残ってくれる感じやもんな」

ハスケル子「はい」
ハスケル子「口頭で気楽に好き勝手にコミュニケーションできないのは不便ですけど」
ハスケル子「でもそれ以上に」
ハスケル子「人に何かを伝えようとした時に、全員が『Slackに投稿する』という統一された手段を取ることで」
ハスケル子「みんなの思考や意見がSlack上で一括管理されていくのは」
ハスケル子「すっごく便利ですよね」

ワイ「ほんまやね」
ワイ「だって、基本的に全てのコミュニケーションが勝手に記録されていくことになる訳やもんね」

「こないだ、〇〇の技術のこと話したけど、どんな内容やったっけ・・・?」

ワイ「って時にも、Slack上で検索できるもんな」

ハスケル子「そうなんです」
ハスケル子「みんなが統一された手段を取ることによって」
ハスケル子「全てのコミュニケーションが一箇所に記録され、履歴が残り、後から確認できる
ハスケル子「まるでReduxVuexみたいですよね」

ワイ「ん?どういうこと?」

何がReduxやVuexっぽいのか

ワイ「ReduxやVuexって、Webアプリケーションの状態管理に使われるものやんな?」
ワイ「それのどこがSlackっぽいの?」

ハスケル子「Slackぽいというか」
ハスケル子「ReduxやVuexの───」

状態を変える手段が統一されていて、履歴が残り、後から確認できる

ハスケル子「───そんな所が、さっきの話と似てるなって思ったんです」
ハスケル子「ReduxやVuexって、アプリケーションの状態を変更する時に」
ハスケル子「必ず決まったメソッドを通るじゃないですか」

ワイ「ああ、そういえばそうやな」
ワイ「例えばVuexを使って、カウンターの値を増やしたい場合やったら」

store.commit('increment')

ワイ「↑って風にやるもんな」
ワイ「何らかのデータの状態を変更したい時は」
ワイ「必ずcommitメソッドを通して変更する感じよな」
ワイ「Reduxなら確かdispatchメソッドを通るんよな」

ハスケル子「はい」
ハスケル子「何か状態の変化を起こしたい時には、必ずcommitメソッドを通る
ハスケル子「こう決まっていると、色々と捗るんです」
ハスケル子「例えばそのcommitメソッドに変更ログ保存機能を仕込んでおけば」
ハスケル子「いつどんな変更が起こったかを全て記録して置けるので」

XX時XX分XX秒: incrementが起こったため、countが1に変化
XX時XX分XX秒: incrementが起こったため、countが2に変化
XX時XX分XX秒: decrementが起こったため、countが1に変化
XX時XX分XX秒: resetが起こったため、countが0に変化

ハスケル子「↑こういう記録を見ながらタイムトラベルデバッグができるんです」
ハスケル子「実際にVue.js devtoolsRedux DevToolsにも」
ハスケル子「タイムトラベルデバッグ機能が搭載されています」

ワイ「おお、そうなんや」
ワイ「確かに、状態変化の履歴を見れたら便利そうやなぁ」
ワイ「ワイは今までReduxとかVuex使わずに、普通にコンポーネントの持つ状態を変えてたから」
ワイ「複雑なご注文フォームとか作る時に大変やったんや」

ワイの体験談

ワイ「よっしゃ、ご注文フォームが完成したで!」
ワイ「ほなブラウザ上でテストしていこか!」

ポチポチ・・・

ワイ「あれ、このパターンの操作を行った場合は」
ワイ「税込みの合計金額が11,000円になるはずやったのに」
ワイ「10,500円になっとるやんけ」
ワイ「いつや、いつズレたんや・・・」
ワイ「どこや、どのメソッドが間違っとるんや・・・」

ワイ「↑こんな感じで、状態変化の経緯が分からんくて困ることがよくあったけど」
ワイ「ReduxやVuexを使えば」

XX時XX分XX秒: hogeActionが起こったため、totalPriceが1,500に変化
XX時XX分XX秒: fugaActionが起こったため、totalPriceが10,000に変化
XX時XX分XX秒: bugActionが起こったため、totalPriceが10,500に変化

ワイ「操作後に↑こんな履歴を見返すことができるから」

「おお、fugaActionが起こったタイミングまでは正しく動いてて」
bugActionが起こったタイミングで価格がおかしくなっとるな!」

ワイ「・・・的な感じでデバッグができる訳やな!」
ワイ「それは捗りそうやな」

ハスケル子「はい」
ハスケル子「ちなみにReduxもVuexも、ElmのThe Elm Architectureというパターンに影響を受けて作られています」

ワイ「おお、ハスケル子ちゃんの大好きなElmか」

ハスケル子「はい!」
ハスケル子「それにですね・・・!」
ハスケル子「・・・ってまた講習みたいになっちゃいました」
ハスケル子「偉そうにすみません」

ワイ「いやいや、全然ええで」
ワイ「こちらこそ口臭がキツくてごめんやで」

ハスケル子「さすがに臭いんでそろそろ帰ってリモートワークしていいですか?」

ワイ「お、おう」
ワイ「もちろんやで」
ワイ「なんかゴメンな...」

まとめ

  • 全てのアクションが統一されたツールによって行われると、
    その履歴が一箇所にまとまって記録され、後から確認できて、便利やね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今更ながらReact Nativeに入門してみた①

はじめに

実は最近、React Nativeなるものに入門してしまいました...。

React Nativeとはなんなのか?といったところの説明から、実際に簡易的なTODOアプリを作成するところまでご紹介できればと思います。

では、早速行ってみましょう〜!

React Nativeとは何か

React Nativeとは、

Reactを使って、iOSやAndroidなどのネイティブアプリを開発することができるクロスプラットフォームな開発ができるフレームワークです。

公式サイトはこちら

なぜReact Nativeで作るか

個人的にメリットだと感じる点はいくつかあります。

  1. React(JavaScript)で書ける
  2. プラットフォームを意識せず開発できる

普段JavaScriptでバックエンドやフロントエンドを書いている自分にとって、同じような感覚でネイティブアプリも作れてしまうのは非常に魅力的です!

また、本来であればiOSならSwift、AndroidならKotlinなど、プラットフォームごとに言語を使い分けるか、どちらかのプラットフォームに絞って開発していたものが、多少UIを変更するだけで良いのはユーザー獲得という観点でもメリットが大きいように思います。

また、ネイティブアプリだけでなく、WEBアプリ版も作成する時、WEBでもReactを使えば、アプリケーション自体はそのまま使い回し、UIのみ変更すれば良いので、そういう意味でもクロスプラットフォームな開発ができます。

Expoとは何か

React Nativeによるネイティブアプリの開発は、Reactを使えることによってかなり開発しやすくなったとはいえ、普段WEBを開発しているエンジニアにとっては少しハードルが高くなります。

そんなネックを解消してくれるのが、Expoです。

Expoは、React Nativeによる開発をより簡単に、わかりやすくしてくれる開発用ツールです。

どう簡単にしてくれるかというと、

  • ほぼReactでWEBアプリを開発している感覚でコードが書ける
  • iOSアプリの場合、アプリのアップデートが再審査なしで行える

などがあります。

ただ、デメリットとして、

  • 実装できる機能に制限がある

ことは念頭に置いておいた方が良さそうです。

公式サイトはこちら

簡易的なTODOアプリの作成

それでは、早速React Nativeを使ってTODO管理ができるアプリを作ります。

今回はアプリの配信はせず、ローカル環境で動作させるだけに留めます。

また、今回は、

  • 開発のしやすさ
  • 実装したい機能を全て実現可能

ということで、Expoも使っていきます。

まずは公式ドキュメントに沿ってReact NativeとExpoの環境構築を済ませておいてください。

プロジェクトを作成したいディレクトリで、Expoプロジェクトを作成します。

expo init todo_app_expo

プロジェクトを作成したら、プロジェクトのルートディレクトリでExpoを起動します。

expo start

コンポーネントに切り分ける

まずは、作りたいものをなるべく最小単位で、コンポーネント化していきます。

今回はシンプルなTODOアプリなので、必要なコンポーネントは下記の通りです。

  1. ユーザーの入力を受け付けるフォーム&ボタン
  2. 登録されたTODOを表示する場所

もっと細かくコンポーネント化できるかもしれません。

その辺りは追々身につけていきます笑

UIを作成する

次に、コンポーネントごとにまずは画面上に表示させてみましょう。

ExpoでiOSシミュレーターを起動させたら、自動でコードの変更を検知して更新されるような設定になっています。

コンポーネントは、ルートディレクトリ直下にcomponentsディレクトリを作成して、その下に配置していきます。

各コンポーネントの実際のコードは下記の通りです。

./components/Form.tsx

フォームの作成は、React NativeのTextInputを使用します。

また、ボタンはReact NativeのButtonを使用します。

ドキュメントはこちら

コンポーネントのソースは下記の通りです。

./components/Form.tsx
import React from 'react';
import { View, Button, TextInput, StyleSheet } from 'react-native';

const Form = () => {

  const [value, onChangeText] = React.useState('');
  const placeholder = 'テキストを入力してください'

  return (
    <View style={styles.form}>
      <TextInput
        style={styles.form}
        onChangeText={text => onChangeText(text)}
        value={value}
        placeholder={placeholder}
      />
      <Button
        onPress={() => {alert('pressed!')}}
        title="Submit"
        color="blue"
      />
    </View>
  )
}

const styles = StyleSheet.create({
  form: {
    height: 40,
    width: '100%',
    borderColor: 'gray',
    borderWidth: 1,
    margin: 'auto',
  }
})

export default Form;

ボタンを押した時のalertは仮実装なので、後ほどきちんと作ります。

./components/TodoList.tsx

TODO一覧のコンポーネントは、現状ダミーのデータを用意しています。

ロジックとしては、TODOが配列で格納されているので、そのデータをReact NativeのFlatListでループ処理して表示しています。

削除ボタンは現状alert出すだけの仮実装です。

ソースは下記の通りです。

./components/TodoList.tsx
import React from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';

const TodoList = () => {

  const todoList = ['TODO1', 'TODO2', 'TODO3'];

  return (
    <View style={styles.list}>
      <FlatList
        data={todoList}
        renderItem={({item}) => (
          <View>
            <Text style={styles.item}>{item}<Text onPress={() => {alert('delete!')}}> ×</Text></Text>
          </View>
        )}
        keyExtractor={(item, index) => index.toString()}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  list: {
    alignItems: 'center',
    marginBottom: 50
  },
  item: {
    fontSize: 22
  }
})

export default TodoList;

あとは軽く配置を整えてとりあえずUIは完成とします。

現状はこんな感じです。

stateでコンポーネントの状態を保持する

さて、UIが完成したら、実際にTODOアプリとして動作するようにしていきます。

TODOアプリにするためには、

  1. 入力したTODOを登録できる
  2. 入力したTODOが一覧表示される
  3. 完了したTODOを削除できる

上記の要件を満たす必要があります。

また、上記の要件を満たすためには、TODOを一時的なデータとしてクライアント側で保持しておく必要があります。

そういったことを実現するために、React Nativeにはstateという物が用意されています。

stateの公式ドキュメントはこちら

stateとは、各コンポーネントの状態を保持するための機能で、

コンポーネントの状態とは、ここでいうTODOが登録されたり削除されたり、といったことを指します。

では早速、stateを用いてTODOの登録、一覧表示、削除を実装していきましょう。

stateの定義・初期化

まずは、現状のTodoデータは下記のコードで定義しています。

const todoList = ['TODO1', 'TODO2', 'TODO3'];

これをstateを使用するように記述すると、下記のようになります。

const [list, setList] = useState(['TODO1', 'TODO2', 'TODO3']);

TODOリストなので、現状は空の配列でstateの値を初期化しておきましょう。

const [list, setList] = useState([]);

これでコンポーネント内で値を保持しておくことができるようになりました!

stateの値を表示・更新する

次は実際にstateで保持している値を表示して、更新するところまでです。

stateの値を使用するときは、下記のように使用できます。

const [list, setList] = useState([]);

const FuncComponent = () => {
    return(
        <Text>{list}</Text>
    )
}

そして、値の更新は下記のように行います。

const [list, setList] = useState([]);

const FuncComponent = () => {
    setList(['TODO']);
    return(
        <Text>{list}</Text>
    )
}

上記のコードだと、更新された値が表示されています。

少し解説すると、stateを作成するのに使用している「useState」ですが、引数には初期値を渡します。

今回のTODOアプリで言うと、空の配列を渡していますよね。

そして、useStateは実際のstateの値と、stateの値を更新する関数を返します。

上記のコードでは、それを変数に格納しているというわけです。

以上を踏まえて、実際に実装したTODO登録の関数とTODO削除の関数を作成しました。

(TODO登録の関数)
javascript
const addList = (item: string) => {
const newList = [...list, item];
setList(newList);
}

(TODO削除の関数)
javascript
const deleteList = (item: string) => {
const newList = list.filter(li => { return li !== item });
setList(newList);
}

一点注意点としては、stateの値の操作で配列を扱う際、

破壊的なメソッドを使用するとリアクティブにレンダリングされなくなります

ですので、値の追加にはpushなどは使わず、今回のようなスプレッド演算子を使うか、concatなどで追加します。

値の削除も同じように、popやshiftではなく、filterなどを使用すると良いでしょう。

propsで親コンポーネントから子コンポーネントに値を渡す

最後に、切り分けたコンポーネント間で値を受け渡せるようにしましょう。

コンポーネントの階層としては、Appコンポーネントが親コンポーネントとなり、それぞれFormコンポーネントとTodoListコンポーネントを子コンポーネントとしています。

親コンポーネントから子コンポーネントに値を渡す際に使えるのが、propsです。

例えば、親コンポーネントで保持している数字を、子コンポーネントで表示する際は下記のようなコードになります。

(親コンポーネント)

const ParentsFunc = () => {
    const [number, setNumber] = useState(0);

    return (
        <ChildFunc num={number} />
    )
}

(子コンポーネント)

const ChildFunc = (props) => {
    return (
        <Test>{props.num}</Test>
    )
}

上記のように記述することで、親コンポーネントから渡ってきた値を子コンポーネントで使用することができます。

また、

const ChildFunc = ({num}) => {
    return (
        <Test>{num}</Test>
    )
}

propsは受け取る時点で展開しておけば、記述量が少なくなります。

このpropsを利用して、同じ容量で先ほど作成した登録用の関数と削除用の関数を親コンポーネント内で定義し、子コンポーネント内で発火させるように実装していきます。

そしてついに完成しました!

完成品のコードはこちら

終わりに

今回初めてReactNativeを使ってみましたが、ドキュメントも充実していて学びやすかったです。

WEBアプリと比べて、ネイティブアプリの方が一般の方に使ってもらいやすいサービスが作れそうでワクワクします!

次はReduxをある程度使えるようになったら、また記事にしてみようかなあ。

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

【React Hooks】グローバル状態管理にはContextAPIを使おう

Reactでグローバルな状態管理といえばReduxですが、Context APIを使えば似たようなことができるので紹介します。

まずは、グローバル状態を定義します。

Global.js
import React, { useState } from 'react';

export const GlobalContext = React.createContext({});

export const GlobalProvider = ({
    children
}) => {
    const [someState, setSomeState] = useState(null);
    const [someState2, setSomeState2] = useState(null);

    return (
        <GlobalContext.Provider
            value={{
                someState,
                setSomeState,
                someState2,
                setSomeState2
            }}
        >
            {children}
        </GlobalContext.Provider>
    );
};

ここではGlobalContextGlobalProviderの二つをexportします。

次に、アプリケーションのルートにあたるファイル(./src/index.jsまたは./src/App.js)内でGlobalProviderを読み込ませます。

index.js
import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';
import { GlobalProvider } from '@/contexts/Global';

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

あとは、コンポーネント内でGlobalContextを読み込めば、グローバル状態を参照、更新できるようになります。

Page.js
import React, { useEfefct, useContext } from 'react';
import { GlobalContext } from '@/contexts/Global';

export default () => {
    const { someState, setSomeState } = useContext(GlobalContext);

    useEfefct(() => {
        // handler
    }, [someState]);

    return (
        <div>
            {someState}
        </div>
    );
};

Reduxよりもお手軽にグローバルな状態管理ができるようになりました。
個人的にはフローがややこしいReduxよりもこちらの方が便利だと思いました。

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

JavaScript 学習用 関数

基礎

function sayHello(){
    return "Hello";
}
sayHello();

function sayHello(name) {
    // ここに戻り値を書いてください
    return `Hello ${ name }`;
  }

引数がない場合

function sayHello(name) {
// ここに戻り値を書いてください
  if (!name) {
    return "What is your name?";
  } else {
    return "Hello " + name;
  }
}

文字列を数値に

function add(a, b) {
  // ここに戻り値を書いてください
  return parseInt(a, 10) + parseInt(b, 10);
}

配列処理

function sum(array) { 
  let sum = 0;
  for(let i = 0; i < array.length; i++){
    sum += array[i];
  }
  return sum;
}

fizzBuzz

function fizzBuzz(number){
  if(number % 5 === 0 && number % 3 === 0){
    return "fizzBuzz";
  } else if(number % 3 === 0){
    return "Fizz";
  } else if(number % 5 === 0){
    return "Buzz";
  } else{
    return number;
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】DOMの基本を学ぶ

はじめに

Javascriptを学ぶ上で欠かせないDOMについて、基礎的な部分を固めたいのでまとめました。
学習サイトやなんやらでJavaScriptの文法的な基礎を学んだ後、次は?みたいな人(わたし)が多いと思います。
そんな方のステップアップに繋がるのがDOMなのかなーと思ってるので、DOM操作に慣れていければいいなと。

目次

DOMとは
DOMで何ができるようになるのか
要素の取得をする
要素取得のメソッド
要素にアクセスして変更を加える
クラスを操作する

DOMとは

Document Object Model」の略です。

HTMLはHTML文章と呼ばれることもあり、文章=Document。
つまりHTML(XMLなども)オブジェクトとして扱い、それを操作するためのモデルです。
その操作をすることを
DOM API や DOM と呼びます。

DOMで何ができるようになるのか

  • JavaScriptを使ってHTMLの情報を取得・変更・イベントの登録などができる

DOMによって、JavaScriptで動的なページを作ることができるようになるということですね。

要素の取得をする

Google Chromeの検証モード内のConsole上で確認していきます。

HTML情報全て取得する

JavaScript
document

スクリーンショット 2020-03-31 18.01.50.png

コンソールに入力してエンターを押すと、#documentという、HTMLファイル内の情報全てを取得することができます。
スクリーンショット 2020-03-31 16.42.19.png
検証モード内のElementsの内容と一緒です。

headタグやbodyタグなど

JavaScript
document.body

と入力することで取得できます。
スクリーンショット 2020-03-31 18.02.55.png

bodyタグ内の要素

JavaScript
document.body.children

スクリーンショット 2020-03-31 16.53.45.png

childrenとは子要素のことで、bodyの中の子要素全てを配列のような形(HTMLCollectin)で取得することができます。

要素取得のメソッド

querySelecter

JavaScript
document.querySelector()

このquerySelecterメソッドを使用し、HTML内のタグ、id、classなどを指定することでも取得することができます。
スクリーンショット 2020-03-31 17.59.28.png
h1タグで取得しても、idで指定しても、同じ要素が取得できます。

ちなみにquerySelecterは、上から探して最初に見つかった要素ひとつを取得するので
スクリーンショット 2020-03-31 18.08.23.png
リストが複数あっても、一番上しか取得できていないのが分かりますね。

querySelecterAll

JavaScript
document.querySelecterAll()

対してquerySelecterAll指定した要素全てを取得します。
スクリーンショット 2020-03-31 18.10.43.png
NodeListと呼ばれる、配列のような形で取得できます。
ちなみにDOMでは要素のことをnode(ノード)と呼びます。(詳しい説明はここでは割愛)

要素にアクセスして変更を加える

innerHTML

innerHTMLは要素の中身にアクセスすることができます。例えば、h1タグの中身、今回でいうと"DOM操作"という文字列ですね。

JavaScript
const h1 = document.querySelector('h1')
h1.innerHTML

上のように、取得した要素をいったん変数に入れておくと楽です。
スクリーンショット 2020-03-31 18.36.59.png
これで"DOM操作"という文字列が取得できていることが分かります。

このh1.innerHTMLの中身を書き換えます。

JavaScript
h1.innerHTML = '書き換えた'

スクリーンショット 2020-03-31 18.39.00.png

すると、ブラウザ上の文字列が書き換わります。
また、innerHTMLはタグを認識するので、innerHTML内にを記述してCSSなどを指定することもできます。

JavaScript
h1.innerHTML = '書き換えた<span style="color:red">かもね</span>'

スクリーンショット 2020-03-31 18.42.52.png

このように、スタイルが適用されます。

textConetent

textContentも要素の中身にアクセスするのですが、こちらはタグなどを無視したテキスト情報のみにアクセスします。

JavaScript
h1.textContent

スクリーンショット 2020-03-31 18.48.52.png
spanタグの中身は無視されています。

style

styleは文字通り要素のスタイルにアクセスし、変更することができます。

JavaScript
const ul = document.querySelector('ul')
ul.style.color = 'green'

スクリーンショット 2020-03-31 20.38.31.png

ulのstyleを変更してみました。

複数要素を取得した場合

querySelectorAllで要素全てを取得した場合のアクセス方法

添字指定

JavaScript
const li = document.querySelectorAll('li')
li[1].style.textDecoration = 'underline'

上記のようにtext-decorationの指定などはプロパティ名がそのまま使えないものがあるので注意しましょう。

スクリーンショット 2020-03-31 20.51.37.png

配列に格納されているので、添字を指定することでアクセスできました。

ループ文

複数要素全てにアクセスするには、ループ文が有効です。

JavaScript
li.forEach(node => node.style.color = 'purple')

querySelectorAllで取得してきた場合は、NodeListに格納されるので、そのひとつ(node)ずつにアクセスすればよいです。
スクリーンショット 2020-03-31 21.07.51.png
これでli要素全てに適用できました。

クラスを操作する

要素の変更のほかに、ノードに対するクラス情報も取得してそれらを操作することができます。

classList

classListにアクセスし、その要素のクラス情報を取得します。
addメソッドでクラスを追加することができます。

JavaScript
const h1 = document.querySelector('#title')
h1.classList.add('text-size')

スクリーンショット 2020-03-31 21.23.44.png
スクリーンショット 2020-03-31 21.24.04.png
elementsを確認すると、h1タグの中にtext-sizeというクラスが追加されています。

もちろんこの新しく付与されたクラスにプロパティが指定してあればそれが適用されます。
スクリーンショット 2020-03-31 21.28.06.png

またremoveメソッドを用いることで、クラスを除去したり
toggleメソッドではそれが呼ばれるたびにクラスを追加したり除去したりといったことができるようになります。

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

AtCoderにJavaScriptで挑む方法

AtCoderにJavaScript(またはTypeScript)で挑むときに最低限押さえておいた方が良いと思われるポイントを紹介します。競プロ初心者の方が対象です。

その1: 提出言語にJavaScriptでなくTypeScriptを選択する

JavaScriptでコードを記述する場合でも、提出言語はTypeScriptを選択した方が良いです。
image.png

というのも、AtCoderのサポートするJavaScriptランタイムはNode.js v5.12のみであり、これはいまや使いものになりません(例えばアロー関数や分割代入が使えません1)。

TypeScriptもv2.1.6とそれほど新しいわけではありませんが、ES2015が問題なく利用できますし、AtCoderのTSはコンパイラオプションが緩いのでJSのコードがそのまま実行できます。

その2: ts-nodeとonline-judge-toolsを使ってローカルでコーディング/テストする

AtCoderはブラウザ上でコーディングやテストをすることが出来ますが、やはり慣れたエディタで作業したり、テストを効率良く行うためにはローカル環境が便利です。

ここではts-nodeonline-judge-toolsを使った簡単なローカル環境構築方法を紹介します。

Step1. ts-nodeをインストール

前述のとおり提出言語にTypeScriptを選択するので、ローカルでもTSの実行環境があったほうが安心です。
適当な作業用ディレクトリで以下のようにts-nodeをインストールします。

$ npm init -y
$ npm install typescript@2.1.6 ts-node @types/node

Step2. online-judge-tools をインストール

online-judge-toolsは、競技プログラミングを行う上で存在する典型作業を自動化するためのツールです。

色々な機能があるようですが、例えばAtCoderの問題ページにあるテストケース(入出力例)をCLIでダウンロードすることができます。

個人的にはAtCoderのためだけに余計なツールを導入したくなかったのですが、問題を読んだ後に手動でテストケースを用意するのはかなり気持ちの萎える作業ですので、このツールだけはインストールしておいた方が良いと思います。

こちらを参考にインストールを行ってください。

Step3. コーディングとテスト

対象となる問題のテストケースを online-judge-toolsでダウンロードします。

$ oj dl https://atcoder.jp/contests/abs/tasks/practice_1

好みのエディタで問題を解きます。

$ vi main.ts

ts-nodeとonline-judge-toolsでテストを行います(先ほどダウンロードしたテストケースを自動で読み込んでくれます)。

$ oj t -c 'sh -c "npx ts-node main.ts"'
[*] 1 cases found

[*] sample-1
[x] time: 1.705760 sec
[+] AC

[x] slowest: 1.705760 sec  (for sample-1)
[+] test success: 1 cases

テストがパスしたらAtCoderのサイトから提出を行いましょう(提出言語をTypeScriptにするのを忘れずに!)

さいごに

AtCoderではJSやTSはかなりマイナーな言語のようです。

言語仕様レベルの根深い問題もいくつかあるようなのですが、それ以前にNode.jsのバージョンを見てあきらめてしまう人も多いような気がしました(私自身がそうでした)。

この記事を見てAtCoderに挑戦するJSerが増えてくれたら嬉しいです。

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

仮想環境でRails API × Nuxt.jsのアプリケーション開発をしたいが、まずブラウザに表示されない!

自己紹介

  • 現在プログラミングの学習中の者です
  • 言い回しや知識に関して、諸所間違い等あるかと思います
  • その際は、ご指摘いただけますと幸いです

やりたいこと

RailsをAPIとして使用し、Nuxt.jsをフロント側に使用する開発において、
それらを仮想環境で開発を開始すること。

具体的には、@saongtx7様が書いてくださった
こちらの記事を仮想環境で進めたいと思ったことがきっかけです。

[入門]Rails API × Nuxt SPA × Firebase Authで作る Todo Appチュートリアル
https://qiita.com/saongtx7/items/d97ef5aec393e704fd3f

本当に素晴らしいチュートリアルでした。
この場を借りて感謝申し上げます。

問題点

私の場合このような問題が起こりました。

  • Rails側でポート指定をしてもブラウザに表在されない(rails s -b 5000)
  • Rails側でポートを指定せずに起動した場合、Nuxt.jsとポート番号が競合する

結論

1.Vagrantfileへ追記をする

まず、仮想環境のあるディレクトリに移動してください。
Vagrantfileがあると思いますのでテキストエディターで起動し、以下の追記をお願いします。

Vagrantfile

//省略
  config.vm.network "forwarded_port", guest: 3000, host: 3000
  config.vm.network "forwarded_port", guest: 5000, host: 5000 #追記!
//省略

2.Railsで起動をする際に-b 0.0.0.0を指定する。

大前提としてrails new --apiができており、アプリケーションのディレクトリは完成しているものとします。所定の場所に移動したら、ターミナルで以下を入力し、起動してください。

rails s -b 0.0.0.0 -p 5000

3.Nuxt.jsのpackage.jsonを書き換える

こちらも大前提として、npx create-nuxt-appができているものとします。所定の場所に移動する前に、テキストエディターでディレクトリを開き、直下のpackage.jsonを開いてください。

package.json
//省略

  "scripts": {
    "dev": "HOST=0.0.0.0 PORT=3000 nuxt", //変更後
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate"
  },

//省略

これができたら、通常通り
npm run dev
(yarnでインストールしている方は違うかも…?)

そして、locallhost:3000にアクセス。
自分はこれで解決し、無事仮想環境でもRails/Nuxtの同時起動ができました!

参考

初学者ながら加筆させていただいた部分としましては、

  • Rails s時の書き方
  • Vagrantfileの書き方

以上になります。

超独学プログラマ様の以下の記事に救われました。
本当に何日も悩みましたので・・・

Nuxt.jsからRailsへ、初めてのapi通信でHelloを表示しよう
https://blog.cloud-acct.com/posts/spa-nuxt-firstapi/

最後に、初投稿となりますので至らない点もあるかと思います。
何かお気づきの点がございましたら、後学のためご指摘をお願いいたします。
この記事が誰かのためになれば幸いです。

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

fontawesomeをサクッと組み込むためのライブラリ「wonderful.js」を作ってみた

:dog2: ... ステータス(α版)

(機能追加後に随時更新していこうと思っています!が一旦Launchします!)

:book: 背景

チェックボックスやラジオボタンを手軽に(おしゃれに)組み込めないか考えた時に、

以下の選択肢に遭遇しました。

(他にもっと簡単な方法があれば教えて下さい....orz)

fontawesomeはアイコン組み込むのになにかと使用されている方が多いのではと思います。

ちょうど、free版にもチェックボックスのアイコンも存在するので組み合わせて使ってお手軽に実装と行こうではないかと考え立ちました。

ちなみに、checkboxの疑似要素(before)を使ってCSSのみでfontawesomeを組み込む方法も紹介されていたのですが、アイコンサイズとラベルのサイズを調整するのが面倒だったりします。

なので、「jQueryにも依存しない、fontawesomeを手軽に組み込めるJSライブラリー」が欲しいということで作成に至りました!

名前は、awesome = イケてる に対して、 wonderful = 素晴らしい という発想からの、イメージは犬! :dog2: =3

:book: レポジトリ

こちらに公開しました!
wonderful.js | DOC

:book: 使い方

1. fontawesomeを使用する準備をしてください

  • 以下のような形でfontawesomeをimport
  <link rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"
  integrity="sha256-UzFD2WYH2U1dQpKDjjZK72VtPeWP50NoJjd26rnAdUI="
  crossorigin="anonymous"/>

2. レポジトリからwonderful.js をダウンロードして、アプリにimportしてください

(bodyタグの最下部が望ましいです)

<script type="text/javascript" src="wonderful.js"></script>

以上!

デフォルト設定だけで以下のようなデザインに仕上がります

ちょっと貧相ですが、使いたいアイコン、色に変更可能です!

image.png

:book: 設定(Configuration)

Checkbox

設定 デフォルト 説明
targetClass undefined カスタマイズしたい要素に付与するクラス名を指定する
isStack false アイコンを重ねて表示したい場合にtrueを設定する
bfFontClass far fa-square (if:isStack = true then far 'fa-square') アイコンのクラスを設定
bfFontClassStacked far fa-square fa-stack-1x 重ねて表示するアイコンのクラスを設定
bfColor black アイコンの色を設定
bfColorStacked black 重ねて表示するアイコンの色を設定
bfSize 1.5rem アイコンのサイズを設定
bfSizeStacked 1.5rem 重ねて表示するアイコンのサイズを設定
bfMarginRight 8px ラベルとの距離を調整するために設定
afFontClass far fa-square (if:isStack = true then far 'fa-square') アイコンのクラスを設定
afFontClassStacked far fa-square fa-stack-1x 重ねて表示するアイコンのクラスを設定
afColor black アイコンの色を設定
afColorStacked black 重ねて表示するアイコンの色を設定
afSize 1.5rem アイコンのサイズを設定
afSizeStacked 1.5rem 重ねて表示するアイコンのサイズを設定
afMarginRight 8px ラベルとの距離を調整するために設定
labelSize 1rem ラベルの文字サイズを設定

※bf*,af* はcheck前後の設定を表しています。

:book: Other Sample

image.png

:eyes: TBI(今後の展開)

  • Webpackでjavascriptの環境作ってんですが、Typescript化したい
  • Checkboxやinputboxにも対応させる
  • 使いやすいようにリファクタリングする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

静的サイトジェネレーター Gatsby

静的サイトジェネレーターとは?

Static Site Generator(SSG)

WebサイトのHTMLファイルを生成するツールのこと

Wordpressのような従来CMSの仕組みは、MySQLなどのDBをもとに、サーバーでHTMLを生成して返すものだった

それに対し静的サイトジェネレーターは、コンパイル時にGraphQLやAPIからすべてのデータを取得し全てのHTMLを最初に生成する

さらに、生成されたファイルを、Netlifyなどのホスティングサービスを用いて、サーバーレスで公開する仕組みが主流になっている

静的サイトジェネレーターのメリット

  • レスポンスが速い。サーバーでHTMLを動的に生成しないから

  • サーバー代 ¥0✨ サーバーが必要ないため

  • サーバー落ちない。メンテが不要

    ※ ただしNetlifyなどのホスティングサービスが落ちる可能性はあります

  • headless CMS と相性が良い

    headless CMS・・・HTMLなどビュー機能がないCMS。コンテンツを管理する仕組みのみを持ち、APIのみ提供する

  • LPやブログなどに相性が良い

デメリット

  • 膨大なページ量のWebサイトには向かない

  • ビルドの時間がかかる。ページ数が多くなるほど遅くなる

  • 頻繁にデータ更新があるサイトに向かない

    データの更新のたびにビルドを走らせる必要があるため

  • API頻繁にリクエストするサイトには向かない

有名な静的サイトジェネレーター

400を超える数がある

過去2年で、フレームワークが成熟したらしく、代表的なものは以下↓

  • Gatsby

    これが一番有名、欧米で流行っている。

    React.js、Webpack、GraphQL、CSSなどで SPA を作成するのに最適

    Headless CMS、SaaSサービス、API、データベースなどに対応

  • Next, Nuxt

    Gatsbyが登場する前は、Next, Nuxt が主流だった

  • Hugo

  • Jekyll

2020年3月現在、静的ジェネレータ人気順が見れるサイト によると、Javascript 言語では Gatsby と Next.js が人気

Gatsbyの特徴

  • Reactベース

  • GraphQLと相性良い

  • Gatsby, Inc.(2015年設立)が開発、シリコンバレーにある

  • IBM、PayPal、Braun、Airbnb などが利用

静的サイトジェネレータは、もともとNextやNuxtのアイデンティティでしたが

Gatsbyの登場により静的サイトジェネレータは別にNextやNuxtじゃなくてもいいのでは?という風潮になってきた

  • Gatsbyプラグインが多数、npmで公開されていて以下のようなことができる

    TypeScript化, PWA対応, WordPress連携, Contentful連携,GA組み込み などなど その他はこちら参考

Jamstack(ジャムスタック)と Lampstack(ランプスタック)

Gatsbyに代表されるようなモダンなウェブサイトの仕組みのことを指すワードがここ1〜2年で注目されてきた

対義語としてLampstackがある

Jamstack

J ・・・ JavaScript

a ・・・ API

m ・・・ Markup

Webサーバーに依存しない、つまりサーバーレスであるウェブサイトのことをJamstackであると言える

Lampstack

L ・・・ Linux

a ・・・ Apache, Webサーバ

m ・・・ MariaDB・MySQL

p ・・・ PHP・Perl・Python

WordPress のようなサーバーサイドとクライアントサイドが密結合なウェブサイトをLampstackであると言える

Create React App, Nuxt (Next), Gatsby の使い分け

Create React App

すべてCSR(クライアントサイドレンダリング)SEOを捨てることになる

ブラウザベースのWEBアプリケーションに向いている

APIのレスポンスを待ってDOM描画するため、描画まで待ち時間が発生する

コーポレートサイトやLPには不向き

Nuxt (Next)

APIのレスポンス結果も含めてSSRするのでSEOは完璧。

データ更新が多い(APIレスポンスが頻繁に変わる)サイトに向いている

Gatsby

ビルド時点で静的なHTMLを生成する。SEOは完璧。描画や遷移が爆速。

ただし、頻繁なデータ更新には向かない。都度ビルドとデプロイが必要。

Create React App Nuxt Gatsby
SEO ×
頻繁なデータ更新
描画速度
LP ×
CMS系(e.g. ブログ, コーポレートサイト)
WEBアプリ(e.g. TODOアプリ) ×
大規模Webサービス × ×

Gatsbyを試したサンプル

自分の環境で試したい方は

公式ドキュメントのクイックスタートで簡単に試すことができます

https://www.gatsbyjs.org/docs/quick-start/

gatsby-cli をインストールし、開発環境を作るとあらかじめディレクトリ、ファイルなどが用意されます

以下コマンドで開発環境を起動します

$ gatsby develop

少し触ってみたサンプル

遷移には <Link></Link> を使う

pjaxのような挙動をする

カーソルをリンク先に乗せた瞬間に、遷移先のhtmlをprefetchし、DOM書き換えとpushStateにより遷移

これにより爆速で遷移される

GraphQLを試す

起動後

http://localhost:8000/___graphql にアクセスすると、GraphQL Explorerが使える

GitHub GraphQL API と同様、ここでGraphQLにより取得できるデータを試すことができる

GraphQL を用いて.md からデータを取得する

gatsby-source-filesystem というプラグインを使い、ローカルファイルを取得することができる

さらに、gatsby-transformer-remark でマークダウンファイルを解析する。HTML形式へ変換して取得も可能。

$ yarn add gatsby-source-filesystem gatsby-transformer-remark

gatsby-config.js に以下を追記

    // ローカルファイルのデータをGatsbyに渡せるプラグイン
    {
      resolve: "gatsby-source-filesystem",
      options: {
        path: `${__dirname}/blog`,
        name: "blog",
      },
    },

/blog 以下のファイルをgraphQLで取得できるようになる

/blog/hello-world.md を追加

---
title: Hello World this is title.
date: "2020-04-01"
categories: []
---

これは、hello-world.md の本文です。

**太字**

## 見出し

Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。

Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。Hello World。

さらに、/pages/get-markdown.js を追加

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Layout from "../components/layout"

const GetMarkdown = () => {
  // useStaticQuery は gatsby に用意されているメソッド
  // ビルド時にGraphQLでクエリすることができる
  // https://www.gatsbyjs.org/docs/use-static-query/#composing-custom-usestaticquery-hooks
  const data = useStaticQuery(graphql`
    query {
      allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
        totalCount
        edges {
          node {
            id
            html # 本文をHTMLに変換して取得する
            frontmatter {
              title
              date(formatString: "YYYY年MM月DD日")
            }
            excerpt # 本文抜粋
          }
        }
      }
    }
  `);

  console.log('data:', data);

  return (
    <Layout>
      <strong>投稿数 ( {data.allMarkdownRemark.totalCount} ) </strong>
      {data.allMarkdownRemark.edges.map(
        ({
          node: {
            id,
            html,
            frontmatter: { title, date },
            excerpt,
          },
        }) => (
          <div key={id}>
            <div>{date}</div>
            <h2>{title}</h2>
            {/* <p>本文抜粋:{excerpt}</p> */}
            <div dangerouslySetInnerHTML={{ __html: html }} />
          </div>
        )
      )}
    </Layout>
  )
}

export default GetMarkdown;

結果はこちら:https://gatsby-site-umamichi.netlify.com/get-markdown/

Contentful からデータを取得し、DOMに反映する

こちらも、 gatsby-source-contentful というプラグインを用いる

$ yarn add gatsby-source-contentful

gatsby-config.js に以下を追記

    // contenful からデータを取ってくるプラグイン
    {
      resolve: `gatsby-source-contentful`,
      options: {
        spaceId: '**********',
        accessToken: '**********',
      },
    },

spaceId accessToken はContentfulの管理画面から取得できる

今回はシンプルに以下のようなUserデータを作成

name age
john 5
umamichi 27

pages/contentful.js を作成

import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Layout from "../components/layout"

const Contentful = () => {
  const data = useStaticQuery(graphql`
    query {
      # contentful からデータを取得する
      allContentfulUser {
        edges {
          node {
            id
            age
            name
          }
        }
      }
    }
  `);

  console.log('data:', data);

  return (
    <Layout>
      {data.allContentfulUser.edges.map((
        {
          node: { id, name, age }
        }
      ) => (
        <h3 key={id}>
          {name} (age: {age})
        </h3>
      ))}
    </Layout>
  )
}

export default Contentful;

結果はこちら:https://gatsby-site-umamichi.netlify.com/contentful/

試したい方は、詳細な手順はこちらから。
https://qiita.com/ozaki25/items/cf7a0d9cc346e55469bc

ビルドしてNetlifyで公開する

ビルド

$ gatsby build

public ディレクトリにこのようにファイルが生成される

contentfulのデータなども一式組み込まれている

ちなみに、上記のサンプル含めたった4ページで初回ビルドにかかった時間は 27.9s

こちらはまだまだ改善の余地ありそう。というかぜひしていただきたい。

キャッシュされるようで、2回目以降のビルドは 15s くらいに落ち着いた。

Netlifyの公開手順は省略しますが、このあたりの記事を参考にしてみてください。5分で公開完了?

https://gatsby-site-umamichi.netlify.com/

まとめ

  • Gatsbyに代表される静的サイトジェネレーターによりwebpackで環境構築するコストが削減できる

ページ遷移の爆速化、画像の最適化圧縮なども自動で行ってくれる

  • 開発するWebサイトの特性に合わせて、フレームワーク使い分けが重要

  • WordPressなどLampstackなCMSが淘汰される

  • Gatsbyの学習コストは肌感でjQuery習得くらい(Nuxtほど大変ではない)が、山のようにあるプラグインを使いこなせるかが重要

    保守されなくなるプラグインありそうなので、プラグイン選びは慎重にすべき

  • 頻繁にAPIリクエストするような動的Webサイト以外はすべて、Gatsby採用しても良いでしょう

  • HTMLコーダーに求められるスキルが React や Vue 込みになってくる

  • 日本は世界のトレンドから1年くらい遅れる傾向があるので2020年から静的サイトジェネレータがもっと普及していくと予想(期待)

参考

https://cloudlance-motio.work/post/static-site-generator-blog-3/

https://ferret-plus.com/9413

https://snipcart.com/blog/choose-best-static-site-generator

https://qiita.com/uehaj/items/1b7f0a86596353587466

https://qiita.com/hppRC/items/00739eaf9ae7fc95c1ca

https://note.com/erukiti/n/na654ad7bd9bb#PArTE

https://watablogtravel.com/cra-create-react-app-next-js-gatsby%E3%80%90-%E3%81%A9%E3%81%86%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F%E3%80%91/#Nextjs

https://www.wikiwand.com/ja/LAMP_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%90%E3%83%B3%E3%83%89%E3%83%AB)

https://jp.techcrunch.com/2019/09/27/2019-09-26-gatsby-raises-15m-series-a-for-its-modern-web-development-platform/

https://qiita.com/Takumon/items/da8347f81a9f021b637f

https://qiita.com/okumurakengo/items/c34aa980afec9957a928

https://qiita.com/ozaki25/items/cf7a0d9cc346e55469bc

https://watablogtravel.com/cra-create-react-app-next-js-gatsby%E3%80%90-%E3%81%A9%E3%81%86%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F%E3%80%91/

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

Ajax通信 覚書

 序文

 Ajax通信の簡単な覚書のつもりで作成しました。初心者が初心者にもわかりやすいように書いたものなので拙い点は多々あるはずですが、ご容赦ください。

 使用言語

1.JavaScript
2.C#

 サンプル

      function (method, url, param) {
        let defer = $.Deferred();        
        $.ajax({
            type: method,
            url: url,
            data: param,
            success: function (data) {

                    let json = JSON.parse(data);
                    defer.resolve(json);
                    return;

            },
            error: function () {
                alert("通信に失敗しました。");
                defer.reject(false);
            }

        });

 

 処理の概要

1.サーバにjson型のデータを、指定したHttpメソッドで、指定したURLに送信。
2.成功したときと失敗したときで処理が分岐する。
 (ア)成功時
    ・レスポンスされたデータをjson型に変換。
 (イ)失敗時
    ・「通信に失敗しました。」というメッセージ付きの警告が出る。

 単語の解説

  • type: Httpメソッド(POST or GET)
  • url: 処理を担当するメソッド(表記は「../コントローラ名/メソッド名」)
  • data: サーバに送るデータ(json型で送る場合の表記は「json = {'データの名前':データの値(変数など), 'データの名前':データの値(変数など),....}」)
  • success: サーバへの通信が成功した後に起こる処理を記述する。ここでの「data」はサーバからレスポンスされたデータを指す。
  • error: サーバへの通信が失敗した後に起こる処理を記述する。

 終わりに

 大雑把な説明しかしていないので、他の記事やサイトも参考にしていくとよいかと思われます。

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

JavaScript : ページの最下部に来た時にイベントを起こす

スクロールがページの一番下に到達したことをJavaScriptで拾うには以下のようにする。

$(window).on('scroll', function () {
  var doch = $(document).innerHeight(); //ページ全体の高さ
  var winh = $(window).innerHeight(); //ウィンドウの高さ
  var bottom = doch - winh; //ページ全体の高さ - ウィンドウの高さ = ページの最下部位置
  if (bottom <= $(window).scrollTop()) {
    //一番下までスクロールした時に実行
    console.log("最底辺!");
  }
});

一番下まで行かずとも「ページの80%」で動作させたいなら、5行目の if 文を以下のようにする。

if (bottom * 0.8 <= $(window).scrollTop()) {
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactのメインコンセプト要点まとめ(2)

この記事の位置づけ

React公式ドキュメントの「メインコンセプト」について、実際にReactの開発を行う中で特に参考になった章を要点の解説付きで纏めたものである。実際にReact公式ドキュメントを読み解いてもらうのが一番だが、時間がない人に要点だけでも読んでもらえるようにしたいと思い、纏めている。

公式のメインコンセプトでは、以下の章立てで説明をしている。
1. Hello World
2. JSXの導入
3. 要素のレンダー
4. コンポーネントとprops
5. stateとライフサイクル
6. イベント処理
7. 条件付きレンダー
8. リストとkey
9. フォーム
10. stateのリフトアップ
11. コンポジションvs継承
12. Reactの流儀

当記事では、以下の3章分について要点まとめをしている。
4. コンポーネントとprops
5. stateとライフサイクル
6. イベント処理

出典元資料

React公式ドキュメントのメインコンセプト

メインコンセプト

4. コンポーネントとprops

知っておくべきポイント

コンポーネントの種類

Reactのコンポーネントには「関数コンポーネント」と「クラスコンポーネント」の二種類がある。

関数コンポーネント

関数コンポーネントには以下の特徴がある。

  • JavaScriptの関数として定義する
  • propsを引数として受け取ることができる
  • stateは持たない
  • React要素を返す
function-comp.js
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
クラスコンポーネント

クラスコンポーネントには以下の特徴がある。

  • React.Componentクラスを継承したクラスとして定義する
  • propsを引数として受け取ることができる
  • stateを持つ
  • render()関数でReact要素のイミュータブルな置き換えを定義する
関数コンポーネントとクラスコンポーネントの使い分け

以下の基準で関数コンポーネントとクラスコンポーネントを使い分けるのがよいと考えている。

  • コンポーネントに状態を持たせる必要があるか?

    • 状態を持たせる必要がないなら、関数コンポーネントを使用する
    • 状態持たせる必要があるなら、クラスコンポーネントを使用する
  • リファクタリングでstateのリフトアップを実施した場合

    • 子のコンポーネントはstateを持たなくなるので、関数コンポーネントに変更する
    • 親のコンポーネントはstateを持つ必要があるので、クラスコンポーネントを使用する

関数コンポーネントは状態を持たないのでステートレスコンポーネント、クラスコンポーネントは状態を持つのでステートフルコンポーネントと一般的に呼ばれる。

stateのリフトアップでstateはできるだけ親に集約することを推奨している通り、ステートフルコンポーネントはできるだけ少なくし、基本的にステートレスコンポーネントでUIを構成することが望ましい。

ユーザ定義のコンポーネントを使用する

以下の例のように、Welcomeというコンポーネントを定義してレンダリングすることが可能。
ユーザ定義のコンポーネントを定義することで、コンポーネント単位のUI分割や再利用が可能となる。
※以下の例でもWelcomeコンポーネントを3回再利用している

welcom.js
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

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

React公式は、できるだけUI部品を小さなコンポーネントに分割することを推奨している。
その理由としては以下の通り。

  • コンポーネントが大きくなると、React要素内のネストが深くなるため変更が難しくなる
  • 個々のUI部品を再利用するのが困難になる

React公式は、経験則から以下のケースでコンポーネントの分割と再利用を検討すべきとしている。

  • 複数回使われるUI(ButtonPanelAvatarなど)
  • UI自体が複雑である場合(AppCommentなど)

コンポーネントのpropsは変更してはいけない

Reactのルールとして、すべてのReactコンポーネントは自身のpropsを変更してはいけないという取り決めがある。これは、Reactのコンポーネントは純粋関数でなければいけないというルールがあるためである。

純粋関数とは?

渡された値を変更せず、必ず同じ結果を返す関数を純粋関数と呼ぶ。

以下のような関数は、必ずa+bの結果を返すので純粋関数と呼べる。

sum.js
function sum(a, b) {
  return a + b;
}

対して、以下のような関数は渡された値を変更しているため、純粋関数とは呼べない。

withdraw.js
function withdraw(account, amount) {
  account.total -= amount;
}

一言コメント

React公式チュートリアルをやっただけでは、以下の2点は曖昧なままなのでやはりメインコンセプトは読んでおく価値があると思う。

  • Reactコンポーネントには関数コンポーネントとクラスコンポーネントが存在する
  • Reactコンポーネントは純粋関数でなければいけない

5. stateとライフサイクル

知っておくべきポイント

クラスコンポーネントの作成

Reactコンポーネントに状態を持たせる場合はクラスコンポーネントを使うというのは、「4. コンポーネントとprops」の章で紹介したとおり。
クラスコンポーネントは以下のような構成となっている。

clock.js
class Clock extends React.Component {  //ポイント1
  render() {  //ポイント2
    return (  //ポイント3
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock date={new Date()} />,
  document.getElementById('root')
);

ポイントは以下の3点。
1. React.Componentを継承するクラスを作成する
2. render()メソッドを定義し、returnでReact要素を返す
3. コンポーネントのpropsにはthis.propsでアクセスする

クラスコンポーネントにローカルのstateを追加する

上記のクラスコンポーネントには状態をまだ持たせていない。
コンポーネントの状態(ローカルのstate)を持たせると、以下のような構成となる。

clock.js
class Clock extends React.Component {
  constructor(props) {
    super(props);  //ポイント1
    this.state = {date: new Date()};  //ポイント2
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>  //ポイント3
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

ポイントは以下の3点。
1. コンストラクタで親クラスにpropsを渡す
2. コンポーネントのローカルstate(this.state)に値を格納する
3. ローカルstateにはthis.stateでアクセスする

クラスコンポーネントのライフサイクルメソッド

多数のコンポーネントを有するアプリケーションの場合、コンポーネントが不要となったときにコンポーネントが保持しているリソースを開放することが重要。
それを実現するために、Reactのクラスコンポーネントはライフサイクルメソッドという仕組みを持っている。

clock.js
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};  //ポイント1
  }

  componentDidMount() {  //ポイント2
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {  //ポイント3
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

ポイントは以下の2点。

  • Clockコンポーネントのローカルstateに現在日付をセットしている
  • ClockコンポーネントがDOMとして描画された後にcomponentDidMount()メソッドが実行される
    • このことを、Reactではマウント(mounting)と呼ぶ
    • この例では、this.timerIDに1秒のタイマーをセットしている
    • タイマーが1秒過ぎるとtick()が実行され、ローカルstateの日時が更新される
      • これによって、ClockコンポーネントのDOMが描画されてから1秒後に日時が更新されることになる
  • Clockコンポーネントが生成したDOMが削除されるときにcomponentWillUnmount()メソッドが実行される
    • このことを、Reactではアンマウント(unmounting)と呼ぶ
    • この例では、this.timerIDの1秒タイマーをリセットしている
      • これによって、ClockコンポーネントのDOMが描画されるとタイマーがリセットされることになる

stateを正しく使用する

Reactコンポーネントのstateを扱うにあたって、いくつか知っておくべきことがある。

stateは直接変更しない

this.stateの値を直接変更してはいけない。直接変更すると、Reactは値の変更を検知できないためDOMが再レンダーされない。
this.stateの値を直接変更してよいのは、Reactコンポーネントのコンストラクタ内だけである。
(コンストラクタでthis.stateに初期値を設定する処理は再レンダーの必要がないため)

this.stateの値を変更したい場合、this.setState()を使用する。this.setState()による値の変更はReactが検知できるため、値の変更によってDOMの再レンダーが行われる。

ng.js
// this.stateを直接変更するのはコンストラクタ内以外ではNG
this.state.comment = 'Hello';
ok.js
// this.stateの値変更はthis.setState()で行う
this.setState({comment: 'Hello'});
stateの更新は非同期に行われることがある

Reactはthis.propsthis.stateを非同期で更新するため、stateの値変更がthis.propsthis.stateの値に依存していると意図しない動作になる恐れがある。

例えば、以下のケースではthis.state.counterの値変更がthis.state.counterの値とthis.props.incrementの値に依存している。
this.propsthis.stateは非同期で更新されるので、this.state.counterの加算が非同期で実行される前に、次の加算処理が行われる可能性がある。そうすると、処理結果が意図しないものになる恐れがある。

ng.js
this.setState({
  counter: this.state.counter + this.props.increment,
});

この問題を回避するために、this.setState()にはthis.statethis.propsの値を使用しての値変更を行う際に同期的に値変更を行う方法が用意されている。
以下のように、this.setStateに関数(第1引数にstate、第2引数にprops)を渡すことで、stateの値変更は値が確定している状態のstateとpropsが使われる。

ok.js
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
単方向データフロー

いかなるstateも必ず特定のコンポーネントが所有しており、stateから生ずるすべてのデータやUIは、コンポーネントの親子関係における子のコンポーネント側にのみ影響するデータフローとなっている。
このようなデータフローは一般的には単方向データフローと呼ばれている。

コンポーネントのツリー構造において、React公式はデータフローのイメージを「propsをデータが流れ落ちる滝であるとすれば、各コンポーネントのstateは任意の場所から合流してくる追加の水源であり、それもまた下に流れ落ちていくもの」と表現している。
image.png

一言コメント

React公式チュートリアルをやっていれば、stateを直接変更してはならないことは知っているはず。
ただ、ライフサイクルメソッドという機能についてはチュートリアルだけでは知らないままなので、この機能は是非抑えておきたい。

6. イベント処理

知っておくべきポイント

Reactにおけるイベント処理

onclickなどのDOM要素のイベントと同様の動作を、Reactのイベント処理として設定することが可能。

DOM要素のonclickイベントでactivateLasers関数を実行する例
click.js
<button onclick="activateLasers()">
  Activate Lasers
</button>
ReactのonClickイベント処理でactivateLasers関数を実行する例
click.js
<button onClick={activateLasers}>
  Activate Lasers
</button>

JSXのコールバックにおけるthisの扱い

Reactのイベント処理でクラスメソッドを呼び出す場合、以下の3つの方法がある。

方法1:クラスコンポーネントのコンストラクタでthisを束縛しておく

コンストラクター内でthis.handleClickで使用するthisを束縛しておく。
ただ、この方法だとすべてのクラスメソッドについてthisを束縛しておく必要があるので、クラスメソッドの数が多い場合はあまり現実的ではない。

toggle.js
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    this.handleClick = this.handleClick.bind(this);  //コンストラクタで各クラスメソッドにthisをbindする
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}
方法2:パブリッククラスフィールド構文を使用する(Create React Appの場合はデフォルトでこの書き方が可能)

クラスメソッドをアロー演算子の関数として定義しておくことで、thisを予め束縛しておくことが可能。
Create React Appでアプリを初期生成した場合、この構文はBabelで予め有効になっている。

logginbutton.js
class LoggingButton extends React.Component {
  handleClick = () => {  //handleClickメソッドをアロー演算子の関数として定義することでthisを束縛する
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}
方法3:Reactのイベント処理でアロー関数を使用する(パブリッククラスフィールド構文を使用していない場合はこの手法を取る)

JSX内のReactイベント処理をアロー関数で記述することで、thisを束縛した状態でクラスメソッドを呼び出すことができる。

loggingbutton.js
class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>  //Reactイベント呼び出し時にアロー演算子を使用することでthisを束縛する
        Click me
      </button>
    );
  }
}

Reactのイベントハンドラに引数を渡す

Reactのイベント処理に対して、引数を渡すことができる。
第1引数は任意の値、第2引数はReactイベントを渡す。

click.js
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>

一言コメント

Reactによるイベント処理は、クリックしたときやフォーカスが外れたときの動作を定義したいときによく使う。thisの束縛については、Create React Appを使用しているのなら「パブリッククラスフィールド構文」を使用しておけば間違いないと思うが、thisはJSの理解を深めるために避けて通れないので、他の書き方についても理解しておいたほうがよいと思う。
また、Reactのイベントハンドラに引数を渡すのはよくあるので、これも書き方を覚えておいたほうがよい。

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

「コード・コマンドを、クリックでクリップボードにコピーできるサイト」まとめ

はじめに

正式にいつからかは把握してませんが、
最近Qiitaのコードブロックをクリックでコピーできるようになりました。

右上のボタンを押すと、このコードがコピーされる

このUIは、公式ページなどによくある形式 で親しみを感じますが、
具体的なサイトをすぐに思い出せなかったので、一覧を記載します。

他にも知っている人は、是非、編集リクエストをください:bow:

一覧

番外編

コードブロックのクリックが コピー以外のアクション のサイト

さいごに

コピーした後のエフェクトの違いを見るのも面白い。

<CODE> タグに、クリックでコピーできる属性が、HTMLの仕様に入ると、
うれしい人が多いんじゃないかと思う。

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

Electron で `Uncaught EvalError: ` が出るときのチェックポイント

これ↓

スクリーンショット 2020-03-31 17.05.19.png

1. 意図した通りの index.html が読み込まれているか?

  • html-webpack-plugin の出力先とメインプロセスでの呼び出し先が不一致など
webpack.config.js
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),
main.js
  // 正しくは 'dist/index.html'
  win.loadFile('index.html');

2. HTMLヘッダ (http-equiv) に単純なスペルミス

index.html
    <!-- 最低限のセキュリティ設定 -->
    <meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
    <meta http-equiv="X-Content-Security-Policy" content="script-src 'self'" />

3. Webpack の development モードでソースマップを付けていない

  • 稀によくある
  • あまりにも関係なさそうなので見落としがち
webpack.config.js
  devtool: process.env.NODE_ENV === 'development' ? 'inline-source-map' : false,
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React hooks】意外と知らないrefの使い方

Reactでコンポーネントから子コンポーネントや要素などを操作するときに便利なrefだが、
意外に調べても使い方が出てこなかったので、様々な利用シーンに合わせて使い道をまとめてみた。

DOMにアクセス

import React, {
    useRef, useEffect
} from 'react';

const Component = () => {
    const el = useRef(null);

    useEffect(() => {
        console.log(el.current);
    }, []);

    return (
        <div ref={el}>DOM</div>
    );
};

export default Component;

el.currentからdiv要素にアクセスできるようになる。
divの幅、高さを取ってきたり、D3などでDOMにグラフを描画する際に使用する。

子コンポーネントのインスタンスにアクセス

import React, {
    useRef, useEffect
} from 'react';

class Child extends React.Component {
    someFunc = () => {
        return 'sample';
    }

    render() {
        return <div> </div>;
    }
}

const Component = () => {
    const ins = useRef(null);

    useEffect(() => {
        console.log(ins.current);
    }, []);

    return (
        <Child ref={ins} />
    );
};

export default Component;

ins.currentから子コンポーネントChildのインスタンスにアクセスができる。
例えば、ins.current.someFunc()で子コンポーネントの関数を実行することができる。
※関数コンポーネント (functional component)ではインスタンスが作成されないため利用できない。

refのフォワーディング(forwardRef)

import React, {
    useRef, useEffect
} from 'react';

const Child = React.forwardRef((props, ref) => {
    return (
        <div ref={ref}>DOM</div>
    );
});

const Component = () => {
    const el = useRef(null);

    useEffect(() => {
        console.log(el.current);
    }, []);

    return (
        <Child ref={el} />
    );
};

export default Component;

el.currentで親コンポーネントから子コンポーネントを介してdiv要素にアクセスすることができる。
HOC(Higher-Order Component)などでコンポーネントを関数で囲む際などに、refをそのまま受け渡すという目的で利用する。
アプリケーションが複雑になればなるほど重宝する機能。

複数refs

import React, {
    useRef, useEffect
} from 'react';

const data = [0, 1, 2];

const Component = () => {
    const els = useRef([]);
    data.forEach((_, i) => {
        els.current[i] = React.createRef();
    });

    useEffect(() => {
        console.log(els.current);
    }, []);

    return (
        <div>
            {
                data.map((n, i) => {
                    return (
                        <div key={n} ref={els.current[i]} >{n}</div>
                    );
                })
            }
        </div>
    );
};

export default Component;

els.currentにはdiv要素の配列が格納されるようになる。
複数の要素にアクセスが必要なシーンで利用する。

応用編(複数Refs × 複数Refs)

import React, {
    useRef, useEffect
} from 'react';

const data = [0, 1, 2];

const ChildComponent = React.forwardRef(({ val1 }, ref) => {
    const els = useRef([]);
    data.forEach((_, i) => {
        els.current[i] = React.createRef();
    });

    useEffect(() => {
        ref.current = els.current;
        return () => {
            ref.current = null;
        };
    }, []);

    return (
        <div>
            {
                data.map((val2, i) => {
                    return (
                        <div key={val2} ref={els.current[i]}>
                            {val1}
                            -
                            {val2}
                        </div>
                    );
                })
            }
        </div>
    );
});

const Component = () => {
    const els = useRef([]);
    data.forEach((_, i) => {
        els.current[i] = React.createRef();
    });

    useEffect(() => {
        console.log(els.current);
    }, []);

    return (
        <div>
            {
                data.map((val1, i) => {
                    return (
                        <ChildComponent key={val1} val1={val1} ref={els.current[i]} />
                    );
                })
            }
        </div>
    );
};

export default Component;

els.currentには[[div,div,div], [div,div,div], [div,div,div]]みたいな感じで配列の配列でdiv要素が格納される。

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

JSONの基本 データ形式

JSONとは、データ形式の1つで、「JavaScript Object Notation」の略称

データに「キー」があるため、意味がわかりやすく、
コンパクトにデータを表現することができる。

JSON

sample.json
[
    {
        "path": "img/img01.jpg",
        "name": "AJ1",
        "caption": "カラーバリエーション豊富"
    },
    {
        "path": "img/img02.jpg",
        "name": "Dunk",
        "caption": "最近の流行り"
    }
]

データ形式とは、データをどのような形式で記述するかを定めたもので、JSONの他に、
・CSV 「Comma Separated Values」(カンマを用いて管理)
・TSV 「Tab Separated Values」(タブ記号を用いて区切ったデータ形式)
・XML 「Extensible Markup Language」(マークアップ言語)
などが存在する


1, img/img01.jpg, AJ1, カラーバリエーション豊富
2, img/img02.jpg, Dunk, 最近の流行り

CSVやTSVは各データが何を表しているのかがわかりにくい。


<?xml version="1.0" ?>
<images>
   <image>
      <path>img/img01.jpg</path>
      <caption>カラーバリエーションが豊富</caption>
      <name>AJ1</name>
   </image>
   <image>
      <path>img/img02.jpg</path>
      <caption>最近の流行り</caption>
      <name>Dunk</name>
   </image>
</images>

XMLは自由にタグを作ってデータを整理できるが、長くなりデータ容量が大きくなりがち

そのため、JSONが活用されています


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

Vue.jsによるバリデーションのサンプルを作ってみた(2)

前回実装したリアルタイムのバリデーションがいまいち意図した動作にならなかったので作り直してみました。

See the Pen OJVqPjo by YusukeIkeda (@YusukeIkeda) on CodePen.

watchを使えばよかったみたい

結論から書くと、リアルタイムで入力値を検証するようなフォームを実装する場合はcomputedではなくwatchを使うと良いようです。
computedを使用して実装しようとすると、初回アクセス時の空白の時の入力値が空なのも監視されてしまいますが、watchではうまく動きました。

HTML・CSS

HTML・CSSについては前回と大きな変更はありません。

HTML・CSS
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Vue.jsによるサンプルバリデーション</title>
<style>
[v-cloak] {
  display: none;
}
table td{
  width: 200px;
}
table input[type="text"]{
  width: 100%;
}
</style>
</head>
<body>
<div id="app">
  <div v-cloak>
    <p v-show="checkName.valid">{{checkName.error}}</p>
    <p v-show="checkTel.valid">{{checkTel.error}}</p>
    <p v-show="checkEmail.valid">{{checkEmail.error}}</p>
  </div>
  <table>
    <tr>
        <th>名前:</th>
        <td><input type="text" v-model="checkName.message" value=""></td>
    </tr>
    <tr>
        <th>電話番号:</th>
        <td><input type="text" v-model="checkTel.message" value=""></td>
    </tr>
    <tr>
        <th>メール:</th>
        <td><input type="text" v-model="checkEmail.message" value=""></td>
    </tr>
    </table>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.6/dist/vue.js"></script>
<script src="script.js"></script>
</body>
</html>

JavaScript

JavaScriptについては先程記述したとおり、Vue.jsのバリデーションはcomputedではなくwatchで値を監視するようにしました。それによって、初回アクセス時に入力が空の時にバリデーションに引っかかる事がなくなりました。ちなみに、初回アクセス時にバリデーションを検知させたい場合はimmediate:trueを指定する必要があります。
また、deep:trueはオブジェクトの内側のネストされたプロパティの変更を監視したい時に設定するもので、handlerはウォッチャがオブジェクト内の変更を感知した際に実行する処理を記載する場所となります。

script.js
let validation = (val, type, max) => {
    let checkVal = val.message.length;
    if(checkVal == 0) {
        val.valid = true;
        val.error = `${type}は必須です`;
    } else if(checkVal > max) {
        val.valid = true;
        val.error = `${type}${max}文字以内で入力してください`;
    } else {
        val.error = "";
        val.valid = false;
    }
}

let vm = new Vue({
  el: '#app',
  data: {
    checkName: {
        message: '',
        error: '',
        valid: false,
    },
    checkTel: {
        message: '',
        error: '',
        valid: false,
    },
    checkEmail: {
        message: '',
        error: '',
        valid: false,
    },
  },
  watch: {
    checkName: {
      handler(val) {
        validation(val, '名前', 10);
      },
      deep: true
    },
    checkTel: {
      handler(val) {
        validation(val, '電話番号', 10);
      },
      deep: true
    },
    checkEmail: {
      handler(val) {
        validation(val, 'メールアドレス', 10);
      },
      deep: true
    },
  }
});

課題とまとめ

実際に運用されているサイトに組み込むには、以下のようなケースも考えて実装する必要がありそうなのでもう少し改良が必要ですが最低限のたたき台はできました。

追加で考える仕様
・すべてのバリデーションを通過したら送信ボタンをアクティブにするような処理。
・正規表現によるバリデーションの実装。
・日付、年齢によるバリデーションの実装。
・電話番号の入力フォームが分割されていた場合の処理。

上に挙げた仕様は頻出するので導入するのであれば事前に実装の方針を考えておいたほうが良さそうです。

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

Vue.jsによるバリデーションのサンプルを作ってみた(改良1)

watchを使えばよかったみたい

Vue.jsによるバリデーションのサンプルを作ってみたで実装したリアルタイムのバリデーションの実装したときはcomputedを使用したのだけどイマイチ意図した動作にならなかったので、watchで作り直してみた。

HTML・CSS

HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Vue.jsによるサンプルバリデーション</title>
<style>
[v-cloak] {
  display: none;
}
table td{
  width: 200px;
}
table input[type="text"]{
  width: 100%;
}
</style>
</head>
<body>
<div id="app">
  <div v-cloak>
    <p v-show="checkName.valid">{{checkName.error}}</p>
    <p v-show="checkTel.valid">{{checkTel.error}}</p>
    <p v-show="checkEmail.valid">{{checkEmail.error}}</p>
  </div>
  <table>
    <tr>
        <th>名前:</th>
        <td><input v-model="checkName.message" /></td>
    </tr>
    <tr>
        <th>電話番号:</th>
        <td><input v-model="checkTel.message" /></td>
    </tr>
    <tr>
        <th>メール:</th>
        <td><input v-model="checkEmail.message" /></td>
    </tr>
    </table>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.6/dist/vue.js"></script>
<script src="script.js"></script>
</body>
</html>

JavaScript

script.js
let validation = (val, type, max) => {
    let checkVal = val.message.length;
    if(checkVal == 0) {
        val.valid = true;
        val.error = type+ "は必須です";
    } else if(checkVal > max) {
        val.valid = true;
        val.error = `${type}${max}文字以内で入力してください`;
    } else {
        val.error = "";
        val.valid = false;
    }
}

let vm = new Vue({
  el: '#app',
  data: {
    checkName: {
        message: '',
        error: '',
        valid: false,
    },
    checkTel: {
        message: '',
        error: '',
        valid: false,
    },
    checkEmail: {
        message: '',
        error: '',
        valid: false,
    },
  },
  watch: {
    checkName: {
      handler(val) {
        validation(val, '名前', 10);
      },
      deep: true
    },
    checkTel: {
      handler(val) {
        validation(val, '電話番号', 10);
      },
      deep: true
    },
    checkEmail: {
      handler(val) {
        validation(val, 'メールアドレス', 10);
      },
      deep: true
    },
  }
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数画像をまとめてアップロードして順にフォームリクエストする

とあるクラウド OCR に画像ファイルを API リクエストする際に、仕様が"multipart/form-data"形式で、更にファイル1つずつのみしか受け付けないものだったので、それに合わせ「画像をサイトにまとめてアップロードした後、ボタン押下で画像をひとつずつ順に API リクエストする」処理を作成しました。備忘録に残しておきます。。。

「input type="file"」の multiple 属性と formData オブジェクトと が鍵。

ローカルから Web サイトに複数ファイルアップロードする HTML

index.html
<input type="button" class="btn btn-primary" value="ファイル選択" onClick="start_img_select();">
<input type="file" name="image_data" multiple="multiple" accept="image/*" onChange="change_img(this)" id="image_data">

「input type="file"」の multiple 属性を"multiple"にすると複数のファイルがアップロードできるようになります。

上記 HTML では、「ファイル選択」ボタンをクリックすると、"start_img_select()"関数で「input type="file"」をクリックしたことにし、ファイル選択ウィンドウを開くようにしています。
「input type="file"」を使用するとファイル選択のウィンドウがデフォルトで出てくるので楽です。

JavaScript 処理

index.js
let ocrcount = 0;
let fileList = [];
let fileCount = 0;

// 「ファイル選択」ボタン押下時
function start_img_select() {
    $("#image_data").css("display", "");
    $("#image_data").trigger('click');
}

// ファイルアップロード後処理
function change_img(e) {
    ocrcount = 0;
    fileList = e.files;
    fileCount = e.files.length;
    let fileName = fileList[ocrcount].name;

    // ファイル読み込みでプレビュー表示
    var reader = new FileReader();
    reader.onload = function (e) {
        $("#preview").attr('src', e.target.result);
    }
    reader.readAsDataURL(fileList[ocrcount]);

    $("#image_data").css("display", "none");
    let fileinfo = `読み込みファイル数: ${ocrcount + 1}/${fileCount}<br>${fileName}`;
    document.getElementById('fileinfo').innerHTML = fileinfo;

    // ファイル送信実行部
    call_execute_form(fileList[ocrcount]);
}

// リクエスト完了後に表示される「次へ」ボタン押下時
function nextOcr() {
    ocrcount = ocrcount + 1;
    let fileName = fileList[ocrcount].name;

    // ファイル読み込みでプレビュー表示
    var reader = new FileReader();
    reader.onload = function (e) {
        $("#preview").attr('src', e.target.result);
    }
    reader.readAsDataURL(fileList[ocrcount]);

    $("#image_data").css("display", "none");
    let fileinfo = `読み込みファイル数: ${ocrcount + 1}/${fileCount}<br>${fileName}`;
    document.getElementById('fileinfo').innerHTML = fileinfo;

    // ファイル送信実行部
    call_execute_form(fileList[ocrcount]);
}

1ファイル目は、そのまま API リクエスト送信。
2ファイル目以降は、「次へ」ボタン押下時にリクエスト送信。

index.js
// API リクエスト処理
function call_execute_form(file) {
    var url = "https://ocrxxx.jp/execute";
    var content_type = "multipart/form-data";
    var encoding = "UTF-8";
    var method = "POST";
    var data = [];

    $("#api_result").text('OCR送信中 ...');
    var fd = new FormData();
    // リクエストに必要なフォーム値をセット
    fd.append("request_id", 1);
    fd.append("image_format", "JPEG");
    fd.append("image_width", 100);
    fd.append("image_height", 100);
    // ...
    fd.append("image_data", file);

    $.ajax({
        url: url,
        contentType: false,
        scriptCharset: encoding,
        type: method,
        processData: false,
        data: fd,
        dataType: 'JSON'
    })
        .done(function (data) {
            if (data.results.length == 0) {
                $("#api_result").text(data.message);
                return;
            }

            var results = data.results[0];
            var ocr_results = results.ocr_results;
            if (ocr_results.length > 0) {

                // 取得データ処理...
                html += "<table>";
                // ...
                html += "</table>";

                // 結果確認後の「次へ」ボタン
                if (ocrcount !== (fileCount - 1)) {
                    html += '<input type="button" class="btn btn-primary" id="nextbtn" value="次へ" onClick="nextOcr();" />';
                }

                $("#api_result").html(html);
            } else {
                $("#api_result").text(JSON.stringify(data));
            }
        })
        .fail(function (data) {
            $("#api_result").text(JSON.stringify(data));
        });
}

OCR で返ってきたレスポンス内容を確認して、次の画像ファイルを API リクエストを行うという処理になってます。

「input type="file"」に複数のファイルをアップロードすると、関数に渡ってきたオブジェクトの"files"プロパティに、ファイル1つずつが配列で格納されているので、1ファイルずつ順に処理しています。

元々、form リクエストを行う際に form タグに囲まれた input の内容をそれぞれ入力しておかなければいけない(もしくは Value 属性に値セットしておかなければいけない)と思い込んでいたのですが、formData オブジェクトを使ってリクエストを行えば、同等の効果が得られるようです。

今回は、わざわざボタン押下で次の画像リクエストに行くようにしていますが、for文で次々リクエストしてデータだけ配列に格納して最後に確認とかでもよかったかなと思います。

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

Chromeでajax処理のデバッグをする

概要

eccube4では商品をカートに入れる際にajax処理を使っているが、エラーになった時に詳細がわからなくて困っていた。

手順

POSTMANを使ったやりかたもあるが、今回はChromeを使った場合の手順。

デベロッパーツールのネットワークタブを選択
Screen Shot 2020-03-31 at 11.50.35.png

この状態でajaxの処理を実行する。
※今回はeccube4の商品詳細のソースなのでjavascriptのリダイレクト処理はコメントアウトなりして無効化しておく必要がある。

//window.location.href = "{{ url('cart') }}";

Nameのところに新しくファイル?が追加されるのでファイルをクリックしてからHeaderのタブを選択する

Screen Shot 2020-03-31 at 12.00.49.png

Formで送信されたデータを知りたい時は下の方にスクロールするとFormDataというのがあるのでそこを見ればいい。

プレビューを選択するとSymfonyでお馴染みのエラー画面が出てくるので、そこを修正していけばおk!
Screen Shot 2020-03-31 at 12.00.49.png

これでajaxのエラーと向き合えるね!!

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

Javascript基本集(5)~Mathについて~

Javascript基本集(5)~Mathについて~

Math」というクラスをご存知でしょうか。
定義した変数などを計算したい場合に、このMathというクラスの中の関数を使って様々な計算をすることが出来ます。
下記はMathクラスに定義された関数の種類です。

abs : 絶対値の計算
(絶対値とは数値を正・負関係なく考えた時の値。例えば”1″と”-1″は絶対値としては、同じ”1″となる。

round : 四捨五入の計算
ceil : 端数切り上げの計算
floor : 端数切り捨ての計算
max : 引数の中で最大の値を返jyouhou返す
random: 乱数を取得
sin : 正弦(サイン)を計算
cos : 余弦(コサイン)を計算
tan : 正接(タンジェント)を計算
asin : 逆正弦(アークサイン)を計算
acos : 逆余弦(アークコサイン)を計算
atan : 逆正接(アークタンジェント)を計算
atan2 : 座標の逆正接(アークタンジェント)を計算
sqrt : 平方根の計算
pow : 累乗の計算
log : 自然対数の計算
exp : eの累乗の計算

ちなみにabsで定義されている絶対値の計算結果については下記のとおりです。

「引数」   「返り値」
スクリーンショット 2020-03-31 11.24.13.png

参考になる記事はこちらが分かりやすいと思います。
Mathクラスについて

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

Javascript基本集(5)~Mathクラスについて~

Javascript基本集(5)~Mathについて~

Math」というクラスをご存知でしょうか。
定義した変数などを計算したい場合に、このMathというクラスの中の関数を使って様々な計算をすることが出来ます。

Mathクラスに定義された関数の種類

Math.〇〇(変数)というように、下記の関数を〇〇に当てはめて引数に入れた内容を計算する。

abs : 絶対値の計算
(絶対値とは数値を正・負関係なく考えた時の値。”1″と”-1″は絶対値としては同じ”1″。)
round : 四捨五入の計算
ceil : 端数切り上げの計算
floor : 端数切り捨ての計算
max : 引数の中で最大の値を返jyouhou返す
random: 乱数を取得
sin : 正弦(サイン)を計算
cos : 余弦(コサイン)を計算
tan : 正接(タンジェント)を計算
asin : 逆正弦(アークサイン)を計算
acos : 逆余弦(アークコサイン)を計算
atan : 逆正接(アークタンジェント)を計算
atan2 : 座標の逆正接(アークタンジェント)を計算
sqrt : 平方根の計算
pow : 累乗の計算
log : 自然対数の計算
exp : eの累乗の計算

absで定義されている絶対値の計算結果

「引数」   「返り値」
スクリーンショット 2020-03-31 11.24.13.png

参考になる記事はこちらが分かりやすいと思います。
Mathクラスについて

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

JavaScriptでHTML要素の読み込み順序からみるを取得する時の注意点

Web初学者です。Ajaxで取得したHTML要素に対して、JavaScriptが効かない現象に直面しました。調べてみると、これは”読み込み順番”と、それに適切なメソッドを使っていないことが原因でした。自分自身が振り返るための意味も含めて、まとめたいと思います。

そもそもブラウザは、HTMLファイルの上から読み込んでいる

そもそもブラウザは、HTMLファイルの上から順番に読み込んでいる。
以下の例では、上から順番に読み込んでくる為、「かきくけこ」が読み込まれる前にjsが実行されます。

foo.html
<h2>あいうえお<2>
<script>alert("hogehoge");</script>
<h2>かきくけこp<2>

もし、jsを別フィアルに記述して様々なHTML要素を取得して処理を行うとき、そもそもjsが実行される時点でHTML要素が読み込まれていないのは困りますよね。先にHTML要素を全て読み込んでてもらう必要があります。そこで、</body>タグの直前にスクリプトを記述します。こうすれば全ての要素が読み終わってからスクリプトが実行されます。(スマホ普及により、HTMLとスタイルシートを優先的に読み込ませる意味もあるようです。)

jQueryのready()メソッドは要素の準備ができた段階で実行される

先ほどの例では、上から順番に読み込んでしまうが故に、要素を取得できないことを示しました。しかし、jQueryのready()メソッドを使用すれば、その問題を解決できます。readyメソッドでは、選択した要素が取得できた時点で、処理を実行します。例えば、

hoge.html
<script type="text/javascript" src="hoge.js"></script>

  <h1 id="piyo">ハローワールド</h1><script type="text/javascript" src="hoge.js"></script>
hoge.js
$(document).ready(function(){
  $("#piyo").css("color","red");
});

スクリプトを①、②のどちらに記述しても実行結果は変わりません。記述場所にかかわらず、要素(id="piyo")がready(準備OK!)できた時点で、実行されるからです。
ちなみに常識だとは思いますが、$(function(){});は、これの省略系です。”おまじない”じゃなりません!僕はしばらくおまじないとして使ってました。笑

本題:Ajaxで追加された要素をどう取得するか

HTML要素は後から追加される場合もある。代表的なのはAjaxによるページの読み込み。追加された新要素に対しては、ready()メソッドだけでは有効ではないです。

そこで使うのが、on()メソッドです。

qiita.html
<h1 id="piyo">ハローワールド</h1>
qiita.js
$(function(){
    $(document).on('click','#piyo',function() {
        $("#piyo").css("color","red");
    });
});

ここではqiita.htmlをajaxで読み込んだと前提します。そのときにon()ならば、無事に要素(id="piyo")を取得できます。on()メソッドは、セレクターが指定した要素(ここでは$(document))の内側にある要素("#piyo")が、クリックされたタイミングで、それがイベント発生する要素かどうか、を判断しているからです。(らしいです。)
 ※たぶん上記のような書き方はしないと思いますが。。あくまでon()の例です。

最後に

jsがうまく働かない場合は、「ちゃんと読み込まれているかな」をまずは疑え!

以上になります。
ご指摘の箇所が多々あると思います。是非コメントよろしくお願いします。

最後まで読んで頂きありがとうございます。
初学者の私ですが、よろしくお願いします。

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

Kinx 実現技術 - Fiber

Fiber

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。作ったものの紹介だけではなく実現のために使った技術を紹介していくのも貢献。その道の人には当たり前でも、そうでない人にも興味をもって貰えるかもしれない。

前回のテーマは Garbage Collection。今回のテーマは Fiber。

Fiber はこれまで使ったことがなかったのだが、途中で関数を中断できる機能は結構便利かも。効果的な使い道は 誰か教えてください

尚、今回の記事を書いている途中で当初考えたやり方ではうまく行かないケースがあり、急遽修正しました。今回は反省の意味を含め、その流れも書いておきます。その方が何やっているか分かりやすそうというのもあり。恥ずかしすぎるが致し方ない。むむむ、見落とした…

実現方法

当初の発想

「基本、その時のフレームと実行再開位置が保存されていればうまくいくんじゃね?」

結果

「うまくいったぜ!チョロいもんだ」

本記事を書いてる途中

「マジか、動かねえ。何でだ?つか、あぁ、なるほど。当たり前だ。なぜ気付かなかった俺。」

改めた発想

「スタック状態と実行再開位置が保存されていればうまくいくに違いない」

結果

「かぁさん、俺やったよ!」

やったこと

当初

  • yield の時の フレーム次回開始位置 を呼び出し元関数オブジェクトに保存。
  • return の際、フレーム情報はクリアせず実行開始位置だけクリアして リターン。これは後で使う。
  • 次回の関数呼び出しの際、関数オブジェクトに フレーム情報があれば yield 後の再開とみなす。
  • 再開と判断した後、実行開始位置がクリアされて いれば return したということで FiberException 例外を発行。

何がダメ?

  • Fiber 中にさらに関数呼び出しされてそこで yield された場合、期待する関数オブジェクトにコンテキストが保存されない。
  • そらそうだ。結局、Fiber 中のスタック状態は全部保存しとかないとダメだった。

具体的にはこの記事の最後の Ruby の例が動かなかったんですね。

修正

  • 関数オブジェクトに ファイバーであるという情報ファイバー開始時のフレーム以降のスタック実行位置 を保持できるフィールドを追加。
  • yield の際、ファイバー開始時のフレーム以降のスタックをコピー し、次の実行開始位置 を記録してリターン。
  • return の際、ファイバーである情報はクリアせず実行開始位置だけクリアして リターン。これは後で使う。
  • 次回の関数呼び出しの際、関数オブジェクトがファイバーとなって いたら yield 後の再開とみなす。
  • 再開と判断した後、実行開始位置がクリアされて いれば return したということで FiberException 例外を発行。
  • resume する際、どこからファイバーをスタートさせたかの情報 をスタック上に残しておく(コピー開始地点の記録)。

解説

スタック状態について

当初はローカル変数がフレームに格納されているので、フレームを復元すれば良いと単純に考えてしまった。こんな感じ。

stack1.png

ところが、Fiber 中に関数呼び出しするとこんな感じに。これは正しく動かない。

stack2.png

これを正しく動かすためにはこうしないといけない。

stack3.png

今回は、スタックにマーキングするようにした。例外スタックのように実行コンテキストに分離する手もあるが何度も保存・復元する必要があるのでスタックのほうが楽。どちらにしても特急で直したためコード的に若干無駄があるので、後でリファクタリングします。

命令追加

もしかしたらもっと簡単にできたかもしれないが、ファイバー開始マークをするためだけの命令が1つ追加されています。もうちょっと汎用的にしたほうが良かったかな。まぁ、まずは動くことが重要なのでこれで。後でリファクタリングもできるし。

謎の特殊命令 _coroutine キーワードが追加されていますが、当然、通常 使ってはいけません 。以下のようになっており、実際、return とほぼ同じ動きをしますが、式を評価する前にスタック上に ファイバー開始したよ! というマークをスタックに残す動作をします。このマークを頼りに、yield するときにどこまでのスタックを保存しておくのかをサーチします。

Fiberクラスの内部定義(ちょっと違うけどイメージ)
class Fiber(fiber) {
    public resume(...arg) {
        _coroutine fiber(...arg);
    }
}

書いててキーワードの位置が意味的に合っていない気がしたので、修正するかも。内部仕様なので、勿論動作は変わらずですが。

GC 対応

あと、忘れてはいけないのが GC でのマーキング。関数オブジェクトがスタックを持っている場合、そこからの参照先にも全部マークさせに行きます。

全部合わせると

具体的な差分は以下です。

はー、やっちまった感が半端ない。一応、直して動くことは確認したけれど。

isAlive が必要とか、ファイバー例外まわりが多少おかしいので、もう少し修正する予定です。

サンプル

Fibonacci

私自身は 良い 使い道がイマイチ(モヤっと)しているのですが、まずはどこかにあった Fiber によるフィボナッチ数列を Kinx で実現してみましょう。

こんな感じに無限ループで yield

var fib = new Fiber(&{
    var a = 0, b = 1;
    while (true) {
        yield b;
        [a, b] = [b, a + b];
    }
});

var r = 35.times().map(&(i) => fib.resume());
r.each(&(v, i) => System.println("fibonacci[%2d] = %7d" % i % v));

実行。

fibonacci[ 0] =       1
fibonacci[ 1] =       1
fibonacci[ 2] =       2
fibonacci[ 3] =       3
fibonacci[ 4] =       5
fibonacci[ 5] =       8
fibonacci[ 6] =      13
fibonacci[ 7] =      21
fibonacci[ 8] =      34
fibonacci[ 9] =      55
fibonacci[10] =      89
fibonacci[11] =     144
fibonacci[12] =     233
fibonacci[13] =     377
fibonacci[14] =     610
fibonacci[15] =     987
fibonacci[16] =    1597
fibonacci[17] =    2584
fibonacci[18] =    4181
fibonacci[19] =    6765
fibonacci[20] =   10946
fibonacci[21] =   17711
fibonacci[22] =   28657
fibonacci[23] =   46368
fibonacci[24] =   75025
fibonacci[25] =  121393
fibonacci[26] =  196418
fibonacci[27] =  317811
fibonacci[28] =  514229
fibonacci[29] =  832040
fibonacci[30] = 1346269
fibonacci[31] = 2178309
fibonacci[32] = 3524578
fibonacci[33] = 5702887
fibonacci[34] = 9227465

よっしゃ、グッジョブ!

Ruby の例をいくつか

Ruby 2.7.0 Fiberクラス より。

Ruby
f = Fiber.new do
  n = 0
  loop do
    Fiber.yield(n)
    n += 1
  end
end

5.times do
 p f.resume
end

#=> 0
    1
    2
    3
    4

これを素直に Kinx に書き直してみる。

Kinx
var f = new Fiber(&{
  var n = 0;
  while (true) {
    yield n;
    n++;
  }
});

5.times(&{
 System.println(f.resume());
});

実行。

0
1
2
3
4

お・ん・な・じー。も一つの例も。

Ruby
def enum2gen(enum)
  Fiber.new do
    enum.each{|i|
      Fiber.yield(i)
    }
  end
end

g = enum2gen(1..100)

p g.resume  #=> 1
p g.resume  #=> 2
p g.resume  #=> 3

ただし、Range オブジェクトは今はないので代替手段で。enum は Kinx では予約語なので変数名も変えておきます。

Kinx
function enum2gen(enumArray) {
  return new Fiber(&{
    enumArray.each(&(i) => {
      yield i;
    });
  });
}

var g = enum2gen(100.times().map(&(i) => i+1));

System.println(g.resume());
System.println(g.resume());
System.println(g.resume());

実行。

1
2
3

やったね。

これが動かなかった…

おわりに

やはりそもそも Fiber を使ったことがない、というのが敗因でしょう。色々使ってみよう。

結局 協調 スレッドなので、実際にスレッドである必要はないし、単一スレッドの中でのコンテキスト(スタック状態)を関数オブジェクトに保持して切り替えている、といった実装になっているだけ。スレッドという言葉に惑わされなければこれで悪くない気がしているのですが、何か根本的なところで思い違いをしているようであれば、ぜひぜひ ご指摘ください

ということで、最初に紹介しようとしたものは紹介しましたね。一先ず、ライブラリの使い方とか、サンプルコードとかを紹介していくフェーズに戻ろうかと思います。

では、次回。

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