Hatena::ブログ(Diary)

Kazzzの日記 このページをアンテナに追加 RSSフィード

2014-05-10

[][]Bootstrap2 → 3への移行

拙作のアプリケーション、Bootstrap2ベースリリースしておりそこから1年も経過していれば気にもならなかっただろうが、開発中にBootstrap3が出てしまったせいで喉に引っかかった小骨のようにずっと気になっていたのだが、この連休が良いチャンスと思い一気にBootstrap3への移行をやってしまうことにした。

移行前のフロントエンドの構成は以下の通り。

ソフトウェア構成(2014/5/1時点)

AngularJS v1.2.13
jquery-2.1.0
jquery.numeric.js 1.3.1
jquery-cookie 0.4
※Twitter Bootstrap v2.3.2
Angular-strap v0.7.4
UI Bootstrap v0.7.0 (TPL有)
TODC Bootstrap v2.3.0
Moment.js 2.1.0
Spin.js 1.3.3
Holder.js 2.2.0
angularLocalStorage.js
sprintf.js 0.0.7
UI.Util 0.1.0

よくもまあ、JavaScriptのライブラリィの衝突の怖さも知らずに揃えた物だと思うが、現行はこの構成で動いている。

この構成でBootstrapをTwitter Bootstrap v2.3.2からTwitter Bootstrap v3.1.1に変更する。

CSS

CSSそのものは各所のBootstrap2→3の移行サイトを参考にさせて頂きそれなりにシミュレーションしたのでそれほど苦労はなかったが、それでも以下は結構時間がかかった。

グリッドシステム

Bootstrap - Grid system
Bootstrapの特徴であるグリッドシステムだが、私のケースでは殆どのspanxxクラス必要に応じてcol-xs-xxかcol-sm-xx(dm)に変更した。 以前のspanxxはBootstrapにレスポンシブを任せるやり方だったが、Bootstrap3では必要な部分と不要な部分でグリッドの適用自分で指定してやる方法になるので、

col-md-xx又はcol-sm-xxで調整

col-xs-xxで調整するか、form-control+指定無し
というのが基本になる。

Form

基本的に各コントロールと対応するラベルを以下のようにrowクラスとcolクラスを設定したdiv要素の階層で書き直した。

<div class="form-group">
    <label class="control-label" for="title">
        タイトル
    </label>
    <div class="row">
        <div class="col-sm-10">
            <input class="form-cntrol" type="text" id="title" ng-model="formData.title" required
                   placeholder="{{ PlaceHolder.title}}" maxlength="100"/>
        </div>
    </div>
</div>

Bootstrap2の時よりも無駄に階層が増えた感じがするが、階層的には理解しやすい。

Bootstrap3のform-controllクラスを指定したINPUT要素はcolクラスの設定無しの場合自動で横一杯に伸びてくれる。なのでcol-sm-xxでデスクトップ上では調整して、それより小さいデバイスでは調整しないことでiPadiPhoneで使えれば良い程度のレスポンシビティを確保できる。

また、新設されたレスポンシビティユーティリティのhedden-xxクラスがとても便利だ。例えば

<a class="navbar-brand hidden-sm" href="./">

等と書くだけで、このアンカーはスクリーン幅が768px〜992pxの間は非表示となる。従来このような振舞にするにはangularJSのフィルタを使うかCSS疑似クラスを用意する必要があった。
Bootstrap - Responsive utilities

input addon

Bootstrapはコントロール同志を前後に連結する"input-prepend input-append"クラスと連結するコントロールを指定する"add-on"の組合せがとても便利で重宝したのだが、この階層の作り方が全く変わってしまい、一から書き直しになってしまった。

    • addon(bootstrap2)
<div class="input-prepend input-append">
  <span class="add-on">@</span>
  <input type="number" class="input-mini text-right">
  <span class="add-on"></span>
</div>       

    • addon(bootstrap3)
<div class="input-group">
  <span class="input-group-addon">@</span>
  <input type="number" class="form-control text-right">
  <span class="input-group-addon"></span>
</div>

なお、input-mini等、input要素の幅を直接調整するクラスは廃止されてしまった?ので、rowとcolのバランスで幅を調整する必要がある。無論インラインスタイルで調整することもできるが、泥縄だしせっかくBootstrapで書くのにレスポンシビティが無くなるのでお勧めできない。

CSSその他

labelやspan、table.trやalertクラス等のセル背景色クラスの名前が(errorがdanger等)変わったのが地味に痛かった。
buttonやlabelのデフォルトクラスが"btn"で良かったのが"btn btn-default"と指定が必須となったのが更に痛かった。

<button class="btn">
f:id:Kazzz:20140510105939p:image
このようにスタイルが反映されない灰色ボタンになってしまう

<button class="btn btn-default">
f:id:Kazzz:20140510105940p:image
これがデフォルトのフラツトフェイスボタン

<button class="btn btn-primary">
f:id:Kazzz:20140510105941p:image
よく見るプライマリ-ボタン

これらはプログラミングで言うところの定数のように使用しており、特にbuttonはModal Dialogを動的に生成する部分で多用していたので、直すのが面倒なのだ。

UI BootstrapとAngularStrapの衝突

両方ともBootstrapをAngularJSで使うための様々なディレクティブが用意されており、私もModalはUI Bootstrapのものを、tooltipとDatePickerはAngularStrapのを愛用していたのだが、この両者、元々衝突することが知られており私がBootstrap2で併用出来ていたのが奇跡だった位である。
Bootstrap3では互いに干渉を避け、使うものだけでカスタム構成を図ったのだが、AngularStrapでtooltipのプロバイダをUI Bootstrapからロードしようとして失敗しているログを見た時に絶望して、すっぱりとAngularStrapを諦めることにした。

AngularStrapのbs-tooltipはUI Bootstrapので容易に交換できるのだが、問題はDatePickerだ。

    • AngularStrapのDatePicker

f:id:Kazzz:20140510015336p:image

    • UI BootstrapのDatePicker

f:id:Kazzz:20140510015335p:image

好みだと思うが、私にはUI BootstrapのDatePickerを選択するセンスはなかったw
とはいうものの、AngularStrapのDatePickerはもう使えない訳で、ならばUI BootstrapのDatePickerを出来るだけシンプルにしてやるしかない訳だ。

幸いにもAngularJSを利用したUI Bootstrapは$templateCacheプロバイダを利用したテンプレートのロード機構採用しており、DatePickerのレイアウト差し替えることが出来る。

書き換えたテンプレートをロードする(AngularJS側)
angular.module('myModule').run(['$templateCache', function($templateCache) {
    //DatePicker用テンプレートキャッシュ入れ替え (ui-bootstrap-tpls-0.10.0.js)
    $templateCache.put("template/datepicker/datepicker.html",
    "<table>\n" +
    "  <thead>\n" +
    "    <tr>\n" +
    "      <th><button type=\"button\" class=\"btn btn-default btn-noBorder btn-sm pull-left\" ng-click=\"move(-1)\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
    "      <th colspan=\"{{rows[0].length - 2 + showWeekNumbers}}\"><button type=\"button\" class=\"btn btn-default btn-noBorder btn-sm btn-block\" ng-click=\"toggleMode()\"><strong>{{title}}</strong></button></th>\n" +
    "      <th><button type=\"button\" class=\"btn btn-default btn-noBorder btn-sm pull-right\" ng-click=\"move(1)\"><i 
class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
    :
    :
   略

これを活用して、以下のようにAngularStrapのDatePicker(というか元はjQueryのDatepickerプラグインだよね)風の落ち着いたL&Fにすることが出来た。
f:id:Kazzz:20140510015337p:image

他にも色々あるのだが、長くなりそうなので割愛する。(特にIEで苦労した件はネタにしたい)
最終的にBootstrap3に移行したことにより、フロントエンドの構成は以下のようになった。

ソフトウェア構成(2014/05/10現在)

AngularJS v1.2.13
jquery-2.1.0
jquery.numeric.js 1.3.1
jquery-cookie 0.4
Twitter Bootstrap v3.1.1
UI Bootstrap v0.10.0 (TPL有)
UI.Utile 0.1.1
Moment.js 2.1.0
Spin.js 1.3.3
Holder.js 2.2.0
angularLocalStorage.js
sprintf.js 0.0.7

結果としてはライブラリィの棚卸にもなったようで、苦労した甲斐があったというものだ。

2013-12-29

[][]小ネタ(TIPS)

気づいたときになるほどと思ったAngularJS(とBootstrap)の小ネタを書いていく。
特に記述の無い場合はstrictモードコントローラコンテキストで実行することを前提にしている。

任意のURLでリロードする
function HogeController($scope, $window) {
    $window.location.href = 'URL'
}
任意のURLで新しいタブを開く(タブブラウザ)
function HogeController($scope, $window) {
    $window.open('URL');
}
フィルタ関数のように実行する
angular.module('MyFilters', []).filter('fl_required', function(Const) {
    return function( input ) {
        return input.required ? label + ' ' + "必須項目" : label;
    };
});

function HogeController($scope, $filter) {
   $scope.label = $filter('fl_required')(input1);
}

フィルタがコントローラから実行できるってのは眼から鱗だったよ。