月別アーカイブ: 2017年3月

UIImageの画像を更新しても反映されない

前提

要素 バージョン
xcode 8.2.1
Swift 3.0.2
iPhone 6s
iOS 10.2.1

問題

例えば以下のような、UIImageViewに描画しているUIImageを動的に書き換える関数がある

public func setImage(newImage: UIImage) {
  self.myImageView.image = newImage
  self.myImageView.setNeedsLayout()
}

通常であれば、この関数を呼び出すとUIImageViewに表示される画像が書き換わるが、
この関数を非同期で呼び出した場合に、変更後の画像が描画されない、もしくは表示までラグが生じてしまう

解決策

どうやら、非同期で画像を更新した場合には、その瞬間に再描画が行われず、別途再描画を行うタイミングで合わせて画像も再描画されるらしい。
とのことなので以下のようにコードを修正し、強制的にメインスレッドで実行することで、非同期で呼び出しても画像が即時更新されるようになった。

public func setImage(newImage: UIImage) {
  DispatchQueue.main.async {
    self.myImageView.image = newImage
    self.myImageView.setNeedsLayout()
  }
}

この問題はUIImageViewに関わらず、UIViewのサブクラス全般で起こりうる問題なので、備忘録として残しておく。

Reactで作る簡易的なTodoList

前提

要素 バージョン
react 15.3.0
babel 5.8.34
Chrome 57.0

概要

  • インフルエンザで死んでたのでリハビリにコーディング
  • Reactをせっかく勉強したので、よく技術のお試し教材に登場する簡易TodoListを作ってみる
  • Reactに関する説明はほぼ無いが、React未経験者でも雰囲気わかる程度には書く
  • JSXを使用する。JavaScriptコード中にXMLが出てくるのは気持ち悪いが、Reactを使うには避けては通れないし手放せないと思う
  • ES6を使用する。というかReactなんてモダンな技術を使ってES6を使わないなんてナンセンスなことはありえないと思う

実装したTodoListは以下の通り。Todoを表示して、追加できて、削除できる。それだけ。編集とか保存とかそんなものはない。

基本的な状態遷移を記述できて、かつ目的がわかりやすいからこうやって教材に選ばれやすいんだと思う。

HTML

特に複雑な内容はないが、やってることは以下の通り

  • React及びReactDOMをロード
  • JSXをトランスパイルするためのbabelをロード
  • UIを動的配置するためのDOM(#content)を定義
  • 最後にscript.jsxをロード
<html lang="ja">

<head>
  <meta charset="utf-8">
  <script src="https://fb.me/react-15.3.0.js"></script>
  <script src="https://fb.me/react-dom-15.3.0.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
  <title>TodoList</title>
</head>

<body>
  <div id="content"></div>
  <script src="script.jsx" type="text/babel"></script>
</body>

</html>

スクリプト(JSX)

  • タスクの一覧と新規タスク(入力内容)をステートとして保持
  • 「追加」ボタン押下時にタスクに新規タスクをpushし、新規タスクをクリア
  • 各タスクのボタン押下で該当タスクを削除
let TodoList = React.createClass({

  /*
   * ステートの初期値を定義
   */
  getInitialState() {
    return {
      newTask: '',
      tasks: [
        '夕飯作る',
        'ケージの掃除する',
        'プログラミングする',
      ]
    };
  },

  /*
   * 新規タスクの入力内容を変更
   */
  changeText(e) {
    this.setState({newTask: e.target.value});
  },

  /*
   * 新規タスクを作成
   */
  addNewTask() {
    let newTasks = this.state.tasks;
    newTasks.push(this.state.newTask);
    this.setState({
      newTask: '',
      tasks: newTasks
    });
  },

  /*
   * 既存タスクを削除
  */
  removeTask(idx) {
    let newTasks = this.state.tasks;
    newTasks.splice(idx, 1);
    this.setState({tasks: newTasks});
  },

  /*
   * UIの描画
   */
  render() {
    let tasks = this.state.tasks.map((t, idx) =>
      <li key={idx}>
        {t}
        <input type='button' value='☓' onClick={() => this.removeTask(idx)} />
      </li>
    );
    return (
      <div>
        <input type='text' value={this.state.newTask} onChange={this.changeText} />
        <input type='button' onClick={this.addNewTask} value="追加"/>
        <ul>
          { tasks }
        </ul>
      </div>
    );
  },
});

/* DOMの描画 */
ReactDOM.render(<TodoList/>, document.getElementById('content'));

Mysqlで大きなデータの高速インポート

様々な解決法がありますが、簡単でしたので共有。
環境:PHP/Mysql
参考:http://qiita.com/studio-kakky/items/a1f943b5476e0cbbe47b

mysqlのデータインポート時、データ量が多くマシンスペックが強く無い場合には
エラーを吐かずウンともスンとも言わない状況に陥る事があると思います。

参考URL内記載のリンク先からBigDump(zipされたPHPファイル)を入手し、
PHPが動く環境でWEBアクセス可能なディレクトリにダンプファイルと共に配置。

アクセスするとダンプファイルがピックアップされているので、
Start Importをクリックしてインポート開始するだけ。

「React開発入門」レビュー

書籍について

タイトル WebデベロッパーのためのReact開発入門 JavaScript UIライブラリの基本と活用
著者 著: 柴田 文彦
発行日 2016/12/01
ページ数 231ページ
価格 2500円
Amazon https://www.amazon.co.jp/dp/4295000337
読了日 2017/03/15

評価

良い点

  • Reactに関して基本的なことが整理されている
  • コード例の構成が全体的に一貫しており、理解しやすい
  • コード例はWebでダウンロードできるので、コード写経もしやすかった
  • React用Chrome拡張の使い方も掲載されており、これがかなり使いやすい。巻末だったが最初に掲載するべき

悪い点

  • 説明が冗長。一度説明した内容をほぼ同じようにまた説明するということがチラホラ
  • 説明がコードの内容をそのまま和訳したようなモノが多い。コードと実行結果を見れば説明を読む必要がない感じ
  • 応用的な例がほぼ無い。Reactがどんなものかはわかったが、それによってどんな楽が出来るかが伝わらない
  • 元々ページ数が少ない上、説明が冗長的なので事実上のコンテンツ量はかなり少ない
  • それで2500円はちょっと

その他

  • 序盤はJSX不使用だが、中盤以降はJSXが必須
  • JSX自体の説明に結構なページ数を割いている
  • Fluxに関しては巻末で簡単に説明する程度

内容とメモなど

1. Reactとは

Reactに関する概要がズラッと書かれている。Reactにおけるキーワード、専門用語も一通り書かれているので、最初に一読、最後にもう一読すると良さそう

JavaScriptフレームワーク全体でのReactの位置付けについても触れているが、比較対象がAngularしか無いのは少し寂しい

2. Reactのコンポーネント

Reactの基本的な使い方全般。この章ではJSXは使われておらず、純粋なReactの機能だけでReactコンポーネントを構築している。

順を追って読んでいるとなんだか不便だなと感じるが、次章でJSXが登場すると突然便利さを感じることができるので、すぐにJSXに飛びつかずにこの章をしっかりやると良さそう

3. JSXの基本

JSXに関する説明、JSXの利用方法、JSXを用いたReactコードの書き方をそれなりのページ数を割いてまとめている。

基本的には前章で作成したコードをJSXを用いたものに書き換えることで、JSXがどれほど便利なのかを主張している。

個人的に、通読前はJavaScript内にXMLが突然あらわれることに強い抵抗を持っていたが、この章を読んでみるとJSXなしのReactは考えられないなと思った

4. Reactを使いこなす

本章では、フォーム要素を対象に、プロパティやステート、単方向データバインディングなど、Reactの具体的な部分に触れている。

Angularの双方向データバインディングに慣れていた自分にとってはすんなり受け入れられたが、そうでもない人にとってはかなり違和感のある章だと思う

5. Reactの一歩進んだ使い方

本章では、以下のようなReact本体ではない外側の要素について触れている

  • アドオン
  • React DeveloperTools
  • Flux

どれも触り程度で具体的な内容はほぼ書かれていないが、React自体に慣れ、ステップアップを詰むためのヒントとなる内容がかかれている。意欲のある人が本書をきっかけに次のステップに進むための足掛けになるとは思う。

所感

本書でなくReactを使ってみた感想

  • 全体的に「あ、この動き、Angularで見たやつだ」となる部分が多かった
  • その上でもReactはUI操作に特化しているので、Angularだけではできそうにない実装ができそう
  • JavaScript中にXMLを書くのに強い抵抗があったが、やってみると別に気にならなくなった
  • JavaScript中にXMLを書くため、デザイナとの作業の分離が難しくなる気はする
  • システム全体で使うよりも、システム内の、UI色の強い一部機能にのみ採用するという使い方ができそう
  • ReactというよりVirtualDOMの考えがスゴイんだ
  • サードパーティ製のアドオンが豊富なようなので、スタイリッシュなUIなど探せば沢山ありそう
  • ChromeのReact Developer Toolsがスゴイ。これはデバッグも捗るし使ってて楽しい
  • jQueryという闇を払拭できそう

MEANでデグー飼育アプリを作ってみた

概要

  • 前々からMEANスタックに興味があった
  • 2017/01/22ぐらいからチマチマMEANアプリ作り始めた
  • とりあえず自分にとって実用的であるのが望ましいので、デグー飼育支援アプリを作った
  • MEANだとこんなことができるよの紹介を含め、動作画面を掲載する
  • 完全に個人用なのでサービスの公開はしないが、ソースコードはGithubで公開している

システム構成

OS + MEAN構成は以下の通り

構成要素 バージョン
Debian 8.7
Mongodb 3.2.9
Express 4.14.0
AngularJS 1.4.1
NodeJS 7.4.0

その他、主に開発に用いたツールなどは以下の通り

構成要素 バージョン
npm 4.1.2
babel 6.22.2
eslint 3.0.1
less 3.3.0
gulp 3.9.1
jQuery 3.1.1
jQUeryUI 1.8.19
bootstrap 3.3.7
d3.js 3.5.16
c3.js 0.4.1

ソースコード

https://github.com/Sa2Knight/degu-log よりクローン可能。

環境構築手順も一応READMEにあるので、頑張ればローカルでも動作させられる(多分)
Zaimアカウント及びZaimAPIを利用するためのトークンが必要だったり敷居は高い。

機能一覧

高速な画面遷移

初っ端から機能というより特徴の話だが、本アプリはSPA(っぽい何か)でありながら、URLを変更しながら画面遷移を行える。
そのため、ブラウザの「戻る」「進む」も利用可能であり、URLを直接指定して特定のリソースにアクセスすることもできる。

AngularJSのオプションモジュールである、ng-routeを用いることで、それを簡単に実装できた。
しかも画面遷移が非常に早い。これだけでMEANで開発してよかったと思えるレベル。

簡易ブログ機能

飼育に関する簡易的なブログっぽいものを投稿することができる。
ブログは通常のCRUDの他、カレンダーを表示して記事を一覧することができる。

体重管理機能

デグーの体重を時系列で管理することができる。便宜上、我が家のデグー2匹(パズー、メイ)に特化しており、それぞれ日付に対する体重を記録することができる。
また、時系列に記録した体重を元に、折れ線グラフをリアルタイムで描画することができる。

ペット関連支出管理機能

本機能は、クラウド家計簿サービスであるZaim及び入力した家計簿記録を取得するZaimAPIを用いて実装している。
日頃から入力している家計簿データを元に、月ごとの支出額の一覧及び特定月の支払内容を閲覧できる。

外部APIの利用もやはりSPAとの相性が抜群だ

写真管理機能

写真をアップロードして管理することができる。写真には、タイトル及び1つ以上のタグを付与することができ、それらで検索することができる。
また、写真アップロード時にオリジナルと別にサムネイルを生成し、写真一覧画面にはサムネイルが表示され、クリックすることでオリジナルサイズを確認できるようにアンっている。

所感

MEANという、MもEもAもNもほぼ使ったこと無い状態から、1つずつ勉強しながらなんとか実用性も多少あるWebアプリを作ることができた。特にSPAを1から作るのは初めての経験だったので、従来型のWebアプリとの違いを感じながら楽しく開発することができた。以下、主な所感を列挙する

  • MongoDBはJavaScriptライクに読み書きを行え、スキーマに依存せずにデータを管理できるので手軽で楽しい
  • AngularJSは双方向データバインディングが一番強いと思ったが、フルスタックのMVCであればAngularJSでなくても良いかなとは思う。
  • Nodeを使えばだいたいのことは対応するNodeモジュールがあって、サーバサイドを一通りJavaScriptで書ける。強い。
  • Expressについては、今回はRestfulなAPIサーバとしてしか使わなかったので触り程度しかわからなかった
  • SPAはクライアントの待機時間が極端に短くなるのでストレス無く利用できることを強く実感
  • クライアントサイドの負担を増やし、サーバサイドの負担を減らす新しい分散システムだと感じた
  • SPAでも画面遷移ごとにURLを変化させたほうが絶対便利だと思った
  • サーバサイドはRestfulなAPIに特化させるように実装したほうが分担が明確で良い
  • JavaScriptはBabelを導入して、全てES6で記述した。やはり最新仕様のJavaScriptは書きやすい。もう戻りたくない。
  • ESLintを導入してコーディング規約を強制した。おかげで比較的高品質のコードを終始簡単に弄るすことができた
  • Bootstrapに頼りきりだった割にUIが雑で残念
  • 一応LESSも導入したが特に使いこなせてない
  • テスト(ユニット,E2E)を書きたかったが、その余裕がなかった。そのうち書きたい
  • とにかく新しい技術/ツールで溢れた開発で楽しい1ヶ月半だった

次はTypeScript及びAngular2に手を付けてみたい。Angular2のほうが良さげであれば、本アプリを移植することも検討する。

express + mongodb でページャ機能を実装

前提

要素 バージョン
node 7.4.0
express 4.14.0
mongodb 3.2.9

概要

  • expressで動作しているAPIサーバに対して、mongodbに格納されているデータをリクエストする際に、ページを指定して取得する要素を絞り込む。
  • ページ番号はクライアントからサーバに対して送信する
  • 1ページあたりのデータ件数はサーバ側で固定する(今回は5件)
  • expressでmongodbを利用するための前準備などは割愛する

使用するmongodbのメソッド

メソッド名 説明
find(query) queryで指定したデータを取得する
skip(n) データを取得する際に先頭N件をスキップする
limit(n) データを取得する際に最大N件までとする

これらを組み合わせて以下のようにチェインすることでページングが実装できる

find(検索条件).skip((ページ番号 – 1) * 1ページあたりの件数).limit(1ページあたりの件数)

実装

今回は、Photoコレクションに含まれる全ての写真データを対象に、ページ番号のみをクライアントから指定して取得する

/* リクエストからページ番号を取得(デフォルト1) */
let page = Number(req.body.page) || 1;

/* 1ページあたりのデータ件数を5件にする */
let numPerPage = 5;

/* 全ての写真を取得 */
let c = collection('photo').find({});

/* 写真の枚数を取得 */
c.count().then(function(count) {

  /* 総ページ数を計算 */
  let pageNum = Math.ceil(count / numPerPage);

  /* 指定されたページ番号が総ページを上回っていた場合総ページ番号で置き換え */
  if (page > pageNum) { page = pageNum; }

  /* ページングを実行 */
  c.skip((page - 1) * numPerPage).limit(numPerPage).toArray(function(err, docs) {
    /* 写真一覧及びページング情報をクライアントに返却 */
    res.send({
      page: page,
      pageNum: pageNum,
      count: count,
      photo: docs,
    });
  });
});

所感

  • ページング処理とSPAの相性は抜群。僅かな待機時間もなくページを切り替えることができて気持ちいい
  • countとtoArrayで非同期処理が2回続いているので、Promissを使えばもっとキレイなコードになると思うが、Promissの使い方が上手く掴めなかったので今回は普通に入れ子を利用した

参考

AngularJSでjQuery datetimepickerを使うためのディレクティブの作成

前提

要素 バージョン
AngularJS 1.4.1
jQuery 3.1.1
jQuery datetimepicker 2.5.4

概要

初めてAngularJSのディレクティブを自作したので舞い上がって投稿する。

本記事では。属性ディレクティブ”dateTimePicker”を定義し、以下のように利用できるようにする

呼び出し

<date-time-picker ng-model="datetime" format="Y/m/d" time=false />

パラメータ

パラメータ名 入力例 説明
ng-model datetime バインディングするAngularJSのデータモデル
format Y/m/d 入力される日時文字列のフォーマット
time true 時刻の入力を行うか

実行例

ディレクティブの作成

パラメータで指定しない部分については、以下の固定値を与えている
– bootstrap用のclassを設定する(form-control)
– AngularJS用のバリデーションを設定(required)
– 各種スクロールをオフにする

angular.module('app').directive('dateTimePicker', function() {
  return {
    restrict: 'E',
    require: 'ngModel',
    scope: {
      format: "@",
      time: "@",
    },
    replace: true,
    template: '<input type="text" class="form-control date-time-picker" required>',
    link(scope, element, attrs, controller) {
      if (scope.format === undefined) {
        scope.format = 'Y/m/d H:i';
      }
      scope.time = scope.time === 'true' ? true : false;
      $(element).datetimepicker({
        format: scope.format,
        timepickerScrollbar: false,
        timepicker: scope.time,
        scrollMonth: false,
        scrollTime: false,
        scrollInput: false,
        onChangeDateTime(dp, $input) {
          controller.$setViewValue($input.val());
        },
      });
    },
  };
});

ソースコード解説

行番号 抜粋 説明
01 directive(‘dateTimePicker’… ディレクティブ dateTimePickerを作成する
03 restrict: ‘E’ <dateTimePicker>の形式で利用できるようにする
04 require: ‘ngModel’ ng-model属性の指定を必須にする
05-08 scope: { 属性(format/time)を文字列で受け取る
09 replace: true, タグをtemplateで置き換える
10 template: ‘<input type=”text”… タグを置き換えるテンプレート
11-27 link(scope, element, attrs, controller) { ディレクティブの挙動を定義
16-26 $(element).datetimepicker({ datetimepickerの呼び出し
24 controller.$setViewValue($input.val()); datetimepickerによって値が書き換わった時に手動でデータバインディングする

実行結果

実行前

<date-time-picker format="Y/m/d H:i" time=true ng-model="blog.post.datetime"/>

実行後

<input type="text" class="form-control date-time-picker ng-isolate-scope ng-valid ng-valid-required ng-touched" required="" format="Y/m/d" time="false" ng-model="weight.post.date">

wordpress 基礎メモ

アイキャッチ画像を有効にする

function.php

// アイキャッチ画像を有効にする。
add_theme_support('post-thumbnails'); 

function.php で、呼び出せるようにしとく。

表示する(代替画像とリンク付き)

index.phpとか

<?php if(have_posts()): while(have_posts()): the_post(); ?>
    <?php if (has_post_thumbnail()) : ?>
        <a href="<?php the_permalink(); ?>"><?php the_post_thumbnail('thumbnail'); ?></a>
    <?php else : ?>
        <a href="<?php the_permalink(); ?>"><img src="<?php bloginfo('template_url'); ?>/img/noimages.png" width="*" height="*" alt="代替画像" /></a>
    <?php endif ; ?>
<?php endwhile; endif; ?>

アイキャッチが設定されてなかったら、/img/noimages.png を表示する、みたいな。

表示サイズ(基礎)

  • thumbnail
  • medium
  • large
  • full
    特に設定しなかったら、これが標準。

「AngularJS アプリケーションプログラミング」レビュー

書籍について

タイトル AngularJS アプリケーションプログラミング
著者 著: 山田 祥寛
発行日 2015/09/20
ページ数 497ページ
価格 3700円
Amazon https://www.amazon.co.jp/dp/4774175684
読了日 2017/03/08

メモなど

  • 前提として、AngularJS(1.4.1)で小さなアプリを開発しつつ、並行して読み進めたため0からのスタートではない
  • AngularJSの全体像を体系的に整理した本
  • 約500ページのボリュームで、AngularJSでできることを一通り解説している
  • この分厚い本を読まずともAngularJSアプリは開発できるが、その原理やより便利な使い方を知るためには必須
  • AngularJSそのものだけでなく、テストフレームワークやビルドツール、Chromeのデベロッパツールなど、AngularJSアプリ開発の周辺技術についても取り上げている
  • いわゆるハンズオンな内容でもなく、電車内で読み進めるだけで理解を深められた
  • AngularJSに興味がなくとも、JavaScriptMVCについて述べられた第1章だけでも読む価値あり

構成と感想

※ 実際の目次から多少弄ってます

1.JavaScriptの歴史

JavaScript誕生の経緯から始まり、不遇の時代を乗り越えてAjaxの登場によって再ブームを起こし、MVCフレームワークが誕生するまでが述べられている。

AngularJSに関心がなくとも、この章だけでも読み物として面白い。何故MVCフレームワークが必要になったのか?そもそもライブラリとフレームワークの違いは何か?フロントエンジニアなら抑えておいて当たり前の内容を丁寧に説明してる。

2.AngularJSの基本的な使い方とそのしくみ

  • モジュール
  • コントローラ
  • サービス
  • ディレクティブ
  • 双方向データバインディング

といった、AngularJSの構成要素である専門用語について、簡単なサンプルコードを通じて解説している。この章を読むだけで、AngularJSがどんなフレームワークなのかが見えてくる。

ここまで読んで「面白そう」と思ったら続きを読めばいいし、興味が沸かなくてもここまで読めばAngularJSとは何なのかの説明ができる。

3.標準ディレクティブ

ディレクティブとは、

<input type="text" ng-model="name">

のように、HTMLを拡張するためのタグや属性のことである。
ディレクティブは自作することもできるが、AngularJSでは多くの便利なディレクティブが提供されている。それらについて、いくつかピックアップして紹介してる。

4.標準フィルター

ビュー側でデータを出力する際に特定の加工を行ってから出力する「ビュー」について。例えば、以下の場合はobjectをJSON化して出力する。

{{ object | json }}

JSONの他にも、数値を丸めたり、大文字小文字を切り替えたりと言った定番の加工を行うフィルタが標準で数多く存在する。それらについて、いくつかピックアップして紹介してる

5.標準サービス

AngularJSの中でも特にコアな部分であるサービス。サービスとはAngularJSにおける「ロジック」を提供する。
MVCで言うならばModelに該当する。(AngularJSはMVWを主張しているが)

6.スコープオブジェクト

スコープはビューとコントローラを結びつけるオブジェクト。AngularJSにおいて非常に重要な役割を持つスコープだが、その使い方と原理を詳細に解説している。

Angular2ではスコープもコントローラも廃止されているらしいのでちょっと残念。

7.自作フィルタ

フィルタの自作について。といっても、フィルタは受け取った値を何らかの加工をして戻すだけなので非常に簡単。

フィルタは高頻度で呼び出されるため、あまり複雑な加工を行うわけにもいかないし、標準フィルタに強力なモノが多いのであまり自作する機会は無いかなと思った。

8.自作サービス

AngularJSアプリの開発=サービスをどんどん自作していくことかなと思う。特にMVCを正確に分離するのであれば、モデルは全てサービスを自前で実装していく必要がある。

サービスには

  • value
  • constant
  • factory
  • service
  • provider

の5種類があり、どれも微妙に特徴が異なる。それぞれについて説明がされているが、まぁfactoryかserviceを使えば良いかなといった感じ。

9.自作ディレクティブ

小規模なアプリ開発であれば縁が無さそうなディレクティブの自作。これを使いこなせればAngularJSを使いこなせてると言っても過言じゃ無さそう。

ディレクティブは出来ることは非常に広く、その分自作に必要なノウハウも広く深い。その分ページ数もかなり割いているが、やはり「ここまでやる必要あるか」と思ってしまう。

よりAngularJSを使い倒したいと思ったらもう一度読んで見ると良さそう。(おそらくその前にAngular2にうつるが)

10. ユニットテスト

AngularJSアプリのユニットテストの行い方について。もちろんテストの手段は様々あるが、本書ではAngularJSでは定番となっている、Karma + jasmineを採用している。

通常のWebアプリケーションのユニットテストとは異なり、依存モジュールを注入したりするなど、AngularJSらしいテスト手法が書かれている。

11. モック

この場合の「モック」とは、ユニットテストを円滑に行うためのダミーオブジェクトである。例えばテスト対象が何らかの外部サービスに依存する時、テスト用の特定の機能のみを持ったダミーオブジェクトを置き換えることで、外部サービスが稼働していなかったり、未実装だった場合でもユニットテストを行うことができる。

AngularJSでは、この「モック」が数多く標準で提供されている。例えば、HTTPリクエストに対するレスポンスを返却するモックでは、テスト対象のコードを弄らずとも、ダミーのHTTPレスポンスを返却するモックをテストコード中で呼び出すことで、レスポンスが返却されたように振る舞うことができる。

12. E2Eテスト

E2Eテストは、実際のユーザがブラウザを操作したときにアプリケーションが正しい振る舞いを行うことを検査する総合的なテストである。

本書では、Protectorというテストランナーを採用し、AngularJSにおけるE2Eテストについて説明している。個人的にはVisualStudioが必要とか書かれているのを見て意欲を失ってしまった。

AngularJSアプリは、原則としてjQueryによるDOMツリーの操作が行われないので、E2Eテストは書きやすいのかなと思った。

13. 関連ライブラリ

AngularJSはそれだけで非常に強力なフルスタックフレームワークだが、局所的な実装を全てサポートしているわけではない。そこで、実装内容に応じて、各種AngularJSライブラリを利用することで、さらにAngularJSアプリの実装の幅を広げることができる。(jQueryにおけるjQueryライブラリのようなもの)

AngularJSでは、フィルタ/ディレクティブ/サービスを自作することができるので、それらを含んだライブラリが(公式/非公式問わず)数多く存在する。本書では以下の一部のみを紹介しているが、他の膨大なライブラリの調べ方なども書いてある。

  • UI Bootstrap
    — Bootstrapをディレクティブで利用できるようにする
  • UI Event
    — 標準以外のイベント処理をできるようにする
  • UI Validate
    — 標準以外の、自作の検証機能を実装できる
  • UI Router
    — 高度なルーティング処理を実装できる。SPAには必須
  • UI Grid
    — グリッド表を生成する
  • angular-translate
    — 国際化対応ページを実装する
  • Angular Google Maps
    — GoogleMapをAngularJSで利用する
  • angular google chart
    — GoogleChartをAngularJSで利用する

14. 関連ツール

AngularJSとは直接関係ないが、AngularJSアプリを開発する上で役立つ以下の関連ツールについて紹介している。もちろんツールの紹介だけでなく、AngularJSで活用するための利用方法についても説明している。

  • Yeoman
    — 様々なフレームワークに対応したアプリの雛形を生成するアプリ。つまりAngularJSアプリの雛形を生成してくれる
  • Grunt
    — タスクランナーツール。TypeScriptのコンパイル、テストの実行、ソースコードのミニファイなどを逐次自動で行ってくれる
  • Bower
    — クライアントサイドのJavaScriptパッケージ管理ツール。ちなみにサーバサイドはnpmが主流

なお、本書ではタスクランナーツールとしてGruntを採用していたが、私はGurpを採用しているのでほぼ飛ばした。

所感

AngularJSは本当に面白い。あと3年ぐらい早く出会っていればこれまでフロントエンドの実装にあんなに苦しまなかった気がする。

しかし、互換性の一切ない、というかアーキテクチャが一新されたAngular2の登場によって少しずつAngularJSが不要になっていくのがとても悲しい。

AngularJSのノウハウがそのままAngular2で生きてくれれば嬉しいが、それはAngular2をやってみないとわからない。

AngularJSからexpressに画像をアップロードする

前提

要素 バージョン
node 7.4.0
npm 4.1.2
express 4.14.0
AngularJS 1.4.1

概要

  • AngularJSのページから画像ファイルをアップロードし、express側で保存させる
  • AngularJSには(少なくとも標準モジュールには)ファイルアップロードの仕組みが存在しないので、ディレクティブを自作して対応する

1.クライアントサイド

添付ファイルをモデルで扱うためのディレクティブを定義

app.directive('fileModel', ['$parse', function($parse) {
  return function(scope, element, attrs) {
    const model = $parse(attrs.fileModel);
    element.bind('change', function() {
      scope.$apply(() => {
        model.assign(scope, element[0].files[0]);
      });
    });
  };
}]);

上記ディレクティブは、以下のように利用する

<input type="file" name="myfile" file-model="my-file">

上記ディレクティブをinput[type=file]にバインドすることで、ファイル選択時にファイルの内容をAngularモデルにバインディングする。
これによって、添付されたファイルをAngularJSのモデルとして操作できるようになる。

ファイルアップロード用のサービスを定義

app.factory('fileUpload' , ['$http' , function($http) {
  return {
    upload(photo) {
      let formData = new FormData();
      formData.append('file' , photo);
      $http.post('/rest/photo/put', formData, {
        headers: {'Content-Type': undefined} ,
        transformRequest: null
      });
    },
  };
}]);

APIがあるであろう、’/rest/photo/put’に対して添付ファイルをPOSTする。
Content-Typeをundefinedに明示的に設定しておくと、AngularJS側で良い感じに設定してくれるので今回はおまかせする。
transformRequestをnullにしておかないと、POSTデータがJSONに変換される可能性があるので、一応明示的にNULLにしておく。

上記サービスを、添付ファイルを指定して実行することで、APIサーバに添付ファイルがPOSTされる

2.サーバサイド

Multerモジュールをインストール

Multerモジュールは、multipart/form-data形式のリクエストを扱うためのNodeJSのミドルウェア。これを用いることでexpress側で添付ファイルを処理することができる。

$ npm install --save multer

POSTされたファイルを保存する

Multerモジュールがインストールできたら、以下のコードをexpress側に追加する(※appはexpressオブジェクト)

var multer  = require('multer');
app.use(multer({ dest: './uploads/'}).any());

非常にシンプルだが、これだけで、アップロードファイルがPOSTされた際に、そのファイルを./uploadsに保存する処理が自動で行われる。

アップロードファイルの検証

アップロードされたファイルは、requestオブジェクトのfiles属性に含まれている。filesは配列であることから、複数ファイルの同時アップロードにも対応できる。

以下のコードでは、アップロードされたファイル(1件)の内容を出力し、無条件でsuccessを戻している。

app.post('/rest/photo/put' , function(req, res) {
  console.log(req.files[0]);
  res.send('success');
});

‘/rest/photo/put’に対して、画像ファイルを1件アップロードすると、以下のログが出力される

{ fieldname: 'file',
  originalname: '10.jpeg',
  encoding: '7bit',
  mimetype: 'image/jpeg',
  destination: './uploads/',
  filename: '652aa507d6e70b89e064bbddd3b33224',
  path: 'uploads/652aa507d6e70b89e064bbddd3b33224',
  size: 5297 }

ユニークなランダム文字列であるfilenameを含んだpathが既に出力に含まれているように、この時点で既に画像ファイルは保存されている。

もちろんファイル名を変更したり、保存する条件を指定したりもできるが、ここでは割愛する。

参考