通貨記号つきで金額を表示
便利。(最近まで知らなかった)
IEは11以上なら大丈夫。
console.log(number.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' })) // → ¥123,457 console.log(new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(number)); // → ¥123,457
When formatting large numbers of numbers, it is better to create a NumberFormat object and use the function provided by its NumberFormat.format property.
ng-tableさわる
準備
webpack×TypeScriptの場合
webpack.config.js
module.exports = { // ... module: { rules: [ { test: /\.tsx?$/, loader: 'ts-loader' }, { test: /\.html$/, use: [ { loader: 'ngtemplate-loader', options: { requireAngular: true, relativeTo: '/src/browser/', prefix: 'ng-table/' } }, { loader: 'html-loader' } ] } ] } // ... };
例
カラムが固定
ng-table="$ctrl.tableParams"
を使う- テーブルヘッダの内容、各セルの調整(formatなど)、ソート設定などはテンプレート上で行える
import { NgTableParams } from 'ng-table'; class Controller { tableParams: any; constructor() { } $onInit() { const data = [ { name: "Moroni", age: 50 }, { name: "test", age: 10 }, { name: "Moroni", age: 50 } ]; this.tableParams = new NgTableParams({ // pagerの設定 (paginationを非表示にする) counts: data.length }, { dataset: data, // pagerの設定 counts: [] }); } } export const TableNormalComponent: ng.IComponentOptions = { controller: Controller, template: ` <table ng-table="$ctrl.tableParams" class="table" show-filter="true"> <!-- $dataでdatasetを参照 --> <tr ng-repeat="user in $data"> <td title="'Name'" filter="{ name: 'text'}" sortable="'name'"> {{user.name}}</td> <td title="'Age'" filter="{ age: 'number'}" sortable="'age'"> {{user.age}}</td> </tr> </table>`, };
カラムが動的に変わる
ng-table-dynamic="$ctrl.tableParams with $ctrl.cols"
を使う- テーブルヘッダの内容、各セルの調整(formatなど)、ソート設定などはjs上で
import { NgTableParams } from 'ng-table'; class Controller { tableParams: any; cols: any; constructor(private NgTableParams) { } $onInit() { const data = [ { name: "Moroni", age: 50 }, { name: "test", age: 10 }, { name: "Moroni", age: 50 } ]; this.cols = [ { field: "name", title: "Name", sortable: "name", show: true }, { field: "age", title: "Age", sortable: "age", show: true }, { field: "money", title: "Money", show: true } ]; this.tableParams = new this.NgTableParams({ // initial sort order sorting: { name: "asc" }, }, { dataset: data, }); } } export const TableDynamicComponent: ng.IComponentOptions = { controller: Controller, template: ` test <table ng-table-dynamic="$ctrl.tableParams with $ctrl.cols" class="table"> <!-- $dataでdatasetを参照 --> <tr ng-repeat="row in $data"> <!-- $columnsでcolumn設定を参照 --> <td ng-repeat="col in $columns">{{row[col.field]}}</td> </tr> </table> `, };
その他
Nested Property
const data = [{ "name": "Martin", "surname": "Freeman", "details": { "country": "Uzbekistan", "personal": { "age": 55 } }, "job": { "money": 564 } } ]; // ng-table ` <tr ng-repeat="row in $data"> <td data-title="'Name'" filter="{name: 'text'}">{{row.name}}</td> <td data-title="'Age'" filter="{'details.personal.age': 'number'}">{{row.details.personal.age}}</td> <td data-title="'Money'">{{row.job.money}}</td> <td data-title="'Country'" filter="{ 'details.country': 'select'}" filter-data="demo.countries">{{row.details.country}}</td> </tr> ` // ng-table-dynamic this.cols = [ { valueExpr: "item.name", title: "Name", show: true }, { valueExpr: "item.details.personal.age", title: "Age", show: true }, { valueExpr: "item.job.money", title: "Money", show: true }, { valueExpr: "item.details.country", title: "Country", show: true } ]; ` <tr ng-repeat="row in $data"> <td title="test" ng-repeat="col in $columns">{{$eval(col.valueExpr, { item: row })}}</td> </tr> `
template-headerのデフォ
header.html
<ng-table-group-row></ng-table-group-row> <ng-table-sorter-row></ng-table-sorter-row> <ng-table-filter-row></ng-table-filter-row>
クライアント側でCSV生成してダウンロードさせる
追記:
Safariも10.1からdownload属性が利用できるにようになりました。
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#Browser_compatibility
CSVの生成
const arr = [ ['ご利用年月日', 'ご利用"箇所', 'ご,利,用,額'], ['2017/01/29', '', '""345'], ['2017/02/01', '"AM"AZON.CO.JP', '7,362'], ]; /** * 各フィールドの囲い -> ダブルクォーテーション * 各フィールドの区切り -> カンマ * 改行コード -> LF */ function arrToCSV(arr) { return arr .map(row => row.map(str => '"' + (str ? str.replace(/"/g, '""') : '') + '"') ) .map(row => row.join(',')) .join('\n'); } arrToCSV(arr); // -> ""ご利用年月日","ご利用""箇所","ご,利,用,額" // "2017/01/29","","""""345" // "2017/02/01","""AM""AZON.CO.JP","7,362""
参考
ダウンロード
以下どれかを利用
- msSaveBlob method - Windows app development
- a 要素 - HTML | MDN のdownload属性
- Data URI scheme - Wikipedia
msSaveBlob
- IE (10以上) で動く
const data = arrToCSV(arr); function download(data, name) { if (window.navigator.msSaveBlob) { // utf8 const bom = '\uFEFF'; const blob = new Blob([bom, data], { type: 'text/csv' }); window.navigator.msSaveBlob(blob, name); } } download(data, 'filename.csv');
download属性
const data = arrToCSV(arr); function download(data, name) { const anchor: any = document.createElement('a'); if (window.URL && anchor.download !== undefined) { // utf8 const bom = '\uFEFF'; const blob = new Blob([bom, data], { type: 'text/csv' }); anchor.download = name; // window.URL.createObjectURLを利用 // https://developer.mozilla.org/ja/docs/Web/API/URL/createObjectURL anchor.href = window.URL.createObjectURL(blob); // これでも可 // anchor.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(bom + data); // firefoxでは一度addしないと動かない document.body.appendChild(anchor); anchor.click(); anchor.parentNode.removeChild(anchor); } } download(data, 'filename.csv');
Data URI scheme + data:attachment/…
- 主にSafari対応
- ファイル名を設定できない
- 動作確認あんまりできていない
const data = arrToCSV(arr); function download(data) { // utf8 const bom = '\uFEFF'; window.location.href = 'data:attachment/csv;charset=utf-8,' + encodeURIComponent(bom + data); } download(data);
以下の方法でもできる(メモ)
// string to base64 function download_base64(data) { const bom = '\uFEFF'; window.location.href = 'data:attachment/csv;charset=utf-8;base64,' + btoa(unescape(encodeURIComponent(bom + data))); } // blob to dataurl function download_filereader(data) { const bom = '\uFEFF'; const reader = new window.FileReader(); const blob = new Blob([bom, data], { type: 'attachment/csv' }); reader.readAsDataURL(blob); reader.onloadend = () => { window.location.href = reader.result; } }
サンプル
その他
参考
Angular UI-Router v1さわる
- 環境は angular-ui-router 1.0.0-rc.1。
- v0.3からMigrateした
- 新機能(uiCanExit, lazyLoadなど)も試したみた
ドキュメント読んだ
About
- About - UI-Router
- Core部分の実装を
ui-router-core
に分けた - AngularJS (1.x), Angular (2.0+), Reactもいける
- Core部分の実装を
- UI-Router Features - UI-Router
- 基本的な機能や名称は今までと同じ
- States, Views, Urls, Parameters, Resolve などなど
- Transitions
- Stateの遷移をコントロールしたい場合Transition Lifecycle Hooksを使う(従来のState Change Eventsは利用しない)
- 基本的な機能や名称は今までと同じ
Tutorials
ボリューム少ないので、さくっとさわれる。
- UI-Router for AngularJS (1.x) - Hello World! - UI-Router
- UI-Router for AngularJS (1.x) - Hello Solar System! - UI-Router
- ComponentsをStateのルートに設定できる
- ui-router/visualizerが面白い
$transition$
The $transition$ is a special injectable object with information about the current state transition.
- UI-Router for AngularJS (1.x) - Hello Galaxy! - UI-Router
- Stateの入れ子
- 親StateからResolved Dataを引き継ぐ
Guide
- URLs, ViewsがOptional
Although states generally have URLs, they are optional.
You might create some child states without URLs, if it doesn’t make sense to make sense to bookmark those child states. The state machine transitions between url-less states as usual, but does not update the url when complete You still get all the other benefits of a state transition such as parameters, resolve, and lifecycle hooks.
Guide: UI-Router 1.0 Migration - UI-Router
New Features
- コンポーネントをルートにすることが可能
- Lazy Loadingに対応
- https://ui-router.github.io/ng1/docs/latest/interfaces/state.statedeclaration.html#lazyload
- lazyLoadにPromiseをわたせばおk
HighChartsを動的読み込みする例
$stateProvider.state('chart', { url: 'chart', component: ChartComponent, // webpackを利用している場合 lazyLoad: () => { return (<any>require).ensure([ 'highcharts' ], require => { (<any>window).Highcharts = require('highcharts'); }); } });
- redirectTo
- 特定条件下で別Stateに遷移させることが可能
- https://ui-router.github.io/ng1/docs/latest/interfaces/state.statedeclaration.html#redirectto
- uiCanExit
- そのStateからExitできるのか、条件を設定できる
- formが編集中なら確認画面をだす、などが可能(今まで
$scope.$on('$stateChangeStart', (event, toState, toParams, fromState, fromParams) => ...
などを使っていた場合は代替できる) - ルートに設定したComponent内で利用できる
- Transition Hooks
- 従来の
$stateChangeStart/$stateChangeSuccess
の代わりに利用できる - なれると便利
- 従来の
- Resolveの実行順が整理された
Breaking changes
- State Change Events(
$stateChangeStart/$stateChangeSuccess
など)が非推奨- デフォルトでは利用できなくなっている
- 代わりにTransition Hookを利用する
- 移行作業はここが一番大変(それでもそれほど時間がかかるわけではない)
Hookの設定
app.run(function($transitions) { /** * 第1引数で遷移対象を設定できる * 第2引数に`TransitionHookFn`を設定 * `TransitionHookFn`は`HookResult`を返す * HookResultについては -> https://ui-router.github.io/ng1/docs/latest/modules/transition.html#hookresult */ $transitions.onStart({}, (trans) => { if (/* 何かしらの条件 */) { return /* true | false | void | TargetState | Promise<true | false | void | TargetState> */ } }); })
- onEnter/onExitの戻り値に意味があるようになったので、意図しない値を返却していないか確認
- $stateParamsが非推奨になった
- 動かないわけではないが、挙動がわかりにくいので代わりに
$transition$
を使ってね
- 動かないわけではないが、挙動がわかりにくいので代わりに
- Default Error Handler
- 今まで見えなかったエラーがコンソールに表示されるかも
- resolve inside views
- マルチviewsを利用している場合に注意
- 今までは各viewにresolveの設定が可能だったが、今後はviewsに一つのresolveになる(stateに一つのresolve)
などなど
Guide: Route to Component - UI-Router
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/) ] };