背景(領域外)をクリックしたら閉じる、とか

ドロップダウンなどを表示したときに、領域外をクリックしたら閉じたい。 HTMLだと、どのように実装するのが一般的なのかわからなかったのでAngularのUI Bootstrapを参考にしてみたよ。

Dropdown
documentにイベントハンドラを登録。

// https://github.com/angular-ui/bootstrap/blob/v1.1.0/src/dropdown/dropdown.js#L13
$document.on('click', closeDropdown);

// ...

var closeDropdown = function(evt) {
  // ...
  // dropdown内をクリックかつ'outsideClick'が設定されている場合は閉じない
  var dropdownElement = openScope.getDropdownElement();
  if (evt && openScope.getAutoClose() === 'outsideClick' && 
  dropdownElement && dropdownElement[0].contains(evt.target)) {
    return;
  }
  // ...
  openScope.isOpen = false;
  // ...
};

補足: dropdownは複数開くことができないようになっている。uibDropdownServiceが管理している。

Modal
自身(全領域カバーしている)にイベントハンドラを登録。

// https://github.com/angular-ui/bootstrap/blob/v1.1.0/src/modal/modal.js#L165
element.on('click', scope.close);

documentに登録する方法だとこんな感じでしょうか。

class Controller {

  closeHandler: () => void;

  /*@ngInject*/
  constructor(
    private $document: ng.IDocumentService,
    private $element: ng.IAugmentedJQuery,
    private $scope: ng.IScope
    ) {
  }

  $onInit() {
    this.closeHandler = () => {
      this.close();
      this.$scope.$apply();
    };
    this.$element.on('click', ($event) => {

      // this.$document.on を無効にするため
      $event.stopPropagation();
    });
  }

  $onDestroy() {
    this.close();
  }

  open() {

    // 何かの処理
    // ...

    this.$document.on('click', this.closeHandler);
  }

  close() {

    // 何かの処理
    // ...

    this.$document.off('click', this.closeHandler);
  }
}

const components: ng.IComponentOptions = {
  templateUrl: 'foo.html',
  controller: Controller,
  bindings: {}
};

export default components;

Angularを1.5から1.6に移行

1.5.6から1.6.1に移行。参考にしたのは

など。

対応箇所

$onInit

初期化のロジックが変更。(bindingsされた変数がconstructor内では利用できなくなった。)
全てのDirectiveとComponentに$onInitを用意し、初期化時の処理を$onInit内に記述。

(この変更は、書き方が統一されるので嬉しい。)

The reasoning behind this change is that it is not idiomatic JavaScript to bind properties to an Object before its constructor has been called ...

$http

$httpのsuccess()error()がなくなった。then()catch()に置き換え。

hash-prefix

hash-prefixが!に変更。

$q

PromiseのRejectionが未処理の場合、警告が出るようになった。

Possibly unhandled rejection:

$uibModalの利用している場合、backdropクリックでモーダルを閉じると警告がでるので、一応以下のように対応。

this.$uibModal
.open({
  templateUrl: 'demo.html',
})
.result.catch(angular.noop);

メモ

errorOnUnhandledRejectionsを利用する。

(この方法だとエラーが捕捉できない)

javascript - Possibly unhandled rejection in Angular 1.6 - Stack Overflow

app.config($qProvider => {
  $qProvider.errorOnUnhandledRejections(false);
})

CircleCIのChromeを最新版に

Chromeのバージョンが低いため、E2Eが失敗してしまう。

Linux Package Signing Keysをインストールしてsudo apt-get --only-upgrade install google-chrome-stableで解決。

dependencies:
  pre
    - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
    - sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
    - sudo apt-get update
    - sudo apt-get --only-upgrade install google-chrome-stable

ユニットテスト(Karma)もTypeScriptで

別記事 -> ユニットテスト(Karma)もTypeScriptで: karma-webpack編 - ryotah’s blog


preprocessorsに追加すればいいだけだった。

GitHub - sergeyt/karma-typescript-preprocessor: TypeScript preprocessor for karma-runner

// karma.conf.js
module.exports = function(config) {
  config.set({
    preprocessors: {
      '**/*.ts': ['typescript']
    },
    typescriptPreprocessor: {
      options: {
        sourceMap: false,
        target: 'es5',
        module: 'commonjs',
      },
      transformPath: function(path) {
        return path.replace(/\.ts$/, '.js');
      }
    },
    plugins: [
      'karma-typescript-preprocessor'
    ]
  });
};

Karmaのバージョンは0.13.22。ちょっと古い。