webpackさわる
環境は webpack 2.2.1。
コード分割
アプリケーション用のjs (app.js)と、ライブラリをまとめたjs (vendor.js)を分割させたい場合。
webpack.optimize.CommonsChunkPlugin
を使えばおk- オフィシャルドキュメント
[chunkhash]
と[hash]
webpack.config.js
var path = require('path'); var webpack = require('webpack'); module.exports = { entry: { app: './src/app', // ここで設定したライブラリは、利用していなくても読み込まれるので注意 vendor: [ 'lodash', 'moment-timezone', ] }, output: { path: path.resolve(__dirname, 'dist'), filename: "[name].js", }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: Infinity }) ] };
動的読み込み
require.ensure
を使えばおk- 古めのブラウザに対応させる場合、PromiseのPolyfillが必要なので注意
- e.g.
import 'es6-promise/auto';
- e.g.
- オフィシャルドキュメント
webpack.config.js
var path = require('path'); var webpack = require('webpack'); module.exports = { entry: { app: './src/app', }, output: { path: path.resolve(__dirname, 'dist'), // webpack.js.org/guides/code-splitting-require/#example // > output.publicPath is an important option when using code-splitting, it is used to tell webpack where to load your bundles on-demand, see the configuration documentation. publicPath: '/app/', filename: '[name].js', chunkFilename: '[name].js' } };
app.js
// a.jsとb.jsを読み込む // require.ensureはPromiseを返す require.ensure(['./a', './b'], require => { // a.jsを実行 // (requireしていないので、b.jsは実行されないまま) require('./a'); console.log('loaded'); }, 'chunk');
TypeScriptを使う場合はrequireを定義する必要がある
// https://github.com/TypeStrong/ts-loader#loading-other-resources-and-code-splitting declare var require: { <T>(path: string): T; (paths: string[], callback: (...modules: any[]) => void): void; ensure: (paths: string[], callback: (require: <T>(path: string) => T) => void) => void; };
その他
moment読み込み時に全てのlocaleファイルを読み込んでしまう
ContextReplacementPlugin
を利用して回避
var webpack = require('webpack'); module.exports = { // ... plugins: [ // moment/locale/ja.jsのみ読み込み new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /ja/) ] };
シェルスクリプトを利用して、tsファイルの先頭にimport文を差し込む
tsファイル内に文字列angular
が存在する場合、ファイルの先頭にimport * as angular from 'angular;
を追加。
# 改行コード LF=$'\\\x0A' # 結果を配列に files=(`find ./client/{app,components} -type f -name "*.ts" -print0 | xargs -0 grep "angular" -l`) for i in "${files[@]}" # 各ファイルの1行目に差し込む do sed -i '' -e "1s/^/import * as angular from 'angular';"$LF"/" $i; # 終了 done;
参考
GithubのLabel設定 Export, Import
Getting Started | GitHub Developer Guide
Create an OAuth token
2段階認証をしている場合。
curl -i -u ${your_username} -H "X-GitHub-OTP: ${your_2fa_OTP_code}" -d '{"scopes": ["repo"], "note": "labels"}' https://api.github.com/authorizations
https://github.com/settings/tokens からでもおk
Export
curl -i -H "Authorization: token ${token}" https://api.github.com/repos/:owner/:repo/labels
https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
Import
curl -i -H "Authorization: token ${token}" -d '{ "name": "foo", "color": "ff0000" }' https://api.github.com/repos/${owner}/${repo}/labels ...
https://developer.github.com/v3/issues/labels/#create-a-label
テーブルとデータ
これは何か
データテーブルを表示する場合、どのようなデータの型が利用しやすいか、について整理してみました。
参考にしたのは以下2つのライブラリ
例
以下のようなテーブルを表示したい。
ID | Date | Name | Age |
---|---|---|---|
1 | March 5, 2017 | Foo | 26 |
2 | March 6, 2017 | Bar | 31 |
データのみ用意
配列
const data = [ [1, 'March 5, 2017', 'Foo', 26], [2, 'March 6, 2017', 'Bar', 31] ];
- 一番シンプル
- カラムタイトルなどは別途用意する必要がある(Htmlのテンプレート内など)
オブジェクト
const data = [ { id: 1, date: 'March 5, 2017', name: 'foo', age: 26}, { id: 2, date: 'March 6, 2017', name: 'bar', age: 31}, ];
- 1行目を利用して、カラムタイトルを作成
- オブジェクトだからカラム項目の順番は保証できないはず
データとカラム用の設定データ
- 汎用性が高いのはこっち
配列
const data = [ [1, 'March 5, 2017', 'Foo', 26], [2, 'March 6, 2017', 'Bar', 31] ]; const columns = [ { name: 'ID'}, { name: 'Date'}, { name: 'Name'}, { name: 'Age'} ];
オブジェクト
const data = [ { id: 1, date: 'March 5, 2017', name: 'foo', age: 26}, { id: 2, date: 'March 6, 2017', name: 'bar', age: 31}, ]; const columns = [ { field: 'id', name: 'ID' }, { field: 'date', name: 'Date' }, { field: 'name', name: 'Name' }, { field: 'age', name: 'Age' } ];
- カラムタイトル(name)とプロパティ名(field)を別にできる
const data = [ { id: 1, date: 'March 5, 2017', customer: { name: 'foo', age: 26 } }, { id: 2, date: 'March 6, 2017', customer: { name: 'bar', age: 31} }, ]; const columns = [ { field: 'id', name: 'ID' }, { field: 'date', name: 'Date' }, { field: 'customer.name', name: 'Name' }, { field: 'customer.age', name: 'Age' } ];
- データのネストなども対応可能
複雑なテーブル
カラム設定データを用意する場合、以下の機能の実現も汎用的にできそう。
- ソート機能のオンオフ
- 合計値の表示
- 平均値の表示
- データと表示内容の変更
2017-03-05
->March 5, 2017
or2017年3月5日
UI-Gridだとこのような感じで設定を追加しています。
// http://ui-grid.info/docs/#/tutorial/105_footer $scope.gridOptions = { showGridFooter: true, showColumnFooter: true, enableFiltering: true, columnDefs: [ { field: 'name', width: '13%' }, { field: 'address.street',aggregationType: uiGridConstants.aggregationTypes.sum, width: '13%' }, { field: 'age', aggregationType: uiGridConstants.aggregationTypes.avg, aggregationHideLabel: true, width: '13%' }, { name: 'ageMin', field: 'age', aggregationType: uiGridConstants.aggregationTypes.min, width: '13%', displayName: 'Age for min' }, { name: 'ageMax', field: 'age', aggregationType: uiGridConstants.aggregationTypes.max, width: '13%', displayName: 'Age for max' }, { name: 'customCellTemplate', field: 'age', width: '14%', footerCellTemplate: '<div class="ui-grid-cell-contents" style="background-color: Red;color: White">custom template</div>' }, { name: 'registered', field: 'registered', width: '20%', cellFilter: 'date', footerCellFilter: 'date', aggregationType: uiGridConstants.aggregationTypes.max } ], data: data, onRegisterApi: function(gridApi) { $scope.gridApi = gridApi; } };
angular-translate読み直し
半年前にまとめたもの。こっちに転載。
これは何か
- 実際に仕事でi18n対応した時に調べたことのメモと設定例
- https://angular-translate.github.io/docs/#/guide を読んで、必要そうな情報を逆引きっぽくまとめたもの
設定例
/*@ngInject*/ export function translateConfig($translateProvider) { $translateProvider // angular-translate-loader-static-files // assets/lang/[langKey].json から非同期で言語ファイルを読み込む .useStaticFilesLoader({ prefix: 'assets/lang/', suffix: '.json' }) // 言語keyの設定 .registerAvailableLanguageKeys(['en', 'ja'], { // en_US などは en と認識させる 'en_*': 'en', 'ja_*': 'ja', // そのほかの言語keyは全て en にする '*': 'en' }) // ブラウザ環境から優先言語を自動取得 // (以前に設定されたkeyがあれば、そちらを優先する <- angular-translate-storage-local利用時) .determinePreferredLanguage() // enable escaping of HTML // https://angular-translate.github.io/docs/#/guide/19_security .useSanitizeValueStrategy('escape') // angular-translate-handler-log .useMissingTranslationHandlerLog() // angular-translate-storage-local .useLocalStorage(); }
以下3つのextensionを利用している
- angular-translate-loader-static-files
- 非同期読み込み
- angular-translate-handler-log
- 対応するメッセージがない場合にエラーを表示
- angular-translate-storage-local
逆引き
言語ファイル(JSON)の書き方
- 通常:
{ "TRANSLATION_ID": "This is a concrete translation for a specific language." }
- ショートカット:
// bar.foo でアクセス可 { "bar": { "foo": { "foo": "This is my text." } } }
- リンク(いい機能だなあ):
{ "SOME_NAMESPACE": { "OK_TEXT": "OK" }, "ANOTHER_NAMESPACE": { "OK_TEXT": "@:SOME_NAMESPACE.OK_TEXT" } }
参考: https://angular-translate.github.io/docs/#/guide/02_getting-started
$translate利用時に、非同期読み込みされたメッセージを反映
- Two-way binding されないので 明示的に処理を書く必要がある
$translateChangeSuccess
を利用
/*@ngInject*/ app.controller('Ctrl', ($scope, $translate, $rootScope) => { $rootScope.$on('$translateChangeSuccess', () => { $translate('HEADLINE').then( // 成功 translation => $scope.headline = translation, // 失敗 translationId => $scope.headline = translationId ); }); });
参考: https://angular-translate.github.io/docs/#/guide/03_using-translate-service
変数を利用
言語JSONファイル:
{ "TRANSLATION_ID": "{{username}} is logged in." }
$translate利用時
$translate('TRANSLATION_ID', { username: 'PascalPrecht' });
$filter利用時
パターン1:
<div>{{ 'TRANSLATION_ID' | translate:'{ username: "PascalPrecht" }' }}</div>
パターン2:
<div>{{ 'TRANSLATION_ID' | translate:translationData }}</div>
angular.module('myApp') /*@ngInject*/ .controller('Ctrl', ($scope) => { $scope.translationData = { username: 'PascalPrecht' }; });
そのほかの方法もある
参考: https://angular-translate.github.io/docs/#/guide/06_variable-replacement
設定されている言語を取得
$rootScope.$on('$translateChangeEnd', (event, args) => { // args.language }); // or $translate.proposedLanguage() || $translate.use() // proposedLanguage() -> 非同期読み込み中の言語keyを取得
利用言語を設定する
preferredLanguage
を利用- ブラウザ環境から優先言語を自動取得する場合は
determinePreferredLanguage
を利用- (内部的に
navigator.languages[0]
navigator.language
などを利用している)
- (内部的に
参考: https://angular-translate.github.io/docs/#/guide/07_multi-language
en_US
en_SG
などをen
として扱う
registerAvailableLanguageKeys
を利用
参考: https://angular-translate.github.io/docs/#/guide/09_language-negotiation
Unit Testing
非同期読み込みをしている場合、ローダー書き換える
beforeEach(module('app', ($provide, $translateProvider) => { $provide.factory('customLoader', $q => () => $q.resolve() ); $translateProvider.useLoader('customLoader'); }));
アプリを起動させる時の流れ
- we register a asynchronous loader
- we define our preferred language
- $translate service is instantiated the first time it gets injected
- angular-translate notices that there’s no language locally available
- it looks if there’s a registered asynchronous loader
- the asynchronous loader is called with the preferred language locale
- the translation data is loaded and ready to be used
参考: https://angular-translate.github.io/docs/#/guide/22_unit-testing-with-angular-translate
タイピングの練習
最近はじめた。仕事の前のウォーミングアップにちょうどよかったりする。
webpack2にした
今の開発環境ではwebpackに任せているタスクが少ないため、かなり簡単に移行できた。
grunt-webpack
を利用してるのでこれもv2に変更。
GitHub - webpack-contrib/grunt-webpack: integrate webpack into grunt build process
webpackで処理が止まってしまったので、keepalive: true
を追加して完了。
(以前から必要だったような気もしないではない)