ITの隊長のブログ

ITの隊長のブログです。Rubyを使って仕事しています。最近も色々やっているお(^ω^ = ^ω^)

【AngularJs1.4.x】input[type="range"]で、何故かレンジ入力の位置が変わらないバグ

丸1日ハマりました。

何故か知らないけど、directiveで作成するhtmlの中のinput[type="range"]。それに初期値を入れようとすると何故か更新されないバグがあることがわかりました。

github.com

issueを見るとわかると思いますが、みんな回避作を色々用意しています。しかし、私の環境では動かない。

どうしたものか。。。

試行錯誤しながら色々試すと、$timeoutを使ったコードが一番しっくりきて、しかも修正できた。

github.com

  • app.js
app.directive('myDirective', ['$timeout', '$interval', function($timeout, $interval) {
    function setScopeValues(scope, attrs) {
        scope.min = attrs.min || 0;
        scope.max = attrs.max || 0;
        scope.value = attrs.value || 0;
    }

    return {
        restrict: 'A',
        require: '?ngModel',
        scope: {
            ngModel: '=',
            ngModelDisplay: '='
        },
        template:
        '<div class="col-sm-12">' +
        '   <input type="text" ng-model="ngModelDisplay">' +
        '</div>' +
        '<div class="col-sm-12">' +
        '       <input type="range" value="{{value}}" class="form-control" min="{{min}}" max="{{max}}" ng-model="ngModel">' +
        '</div>',
        transclude: true,
        link: function(scope, element, attrs, ngModel) {
            setScopeValues(scope, attrs);

            // 下記コードを追加したら修正できた
            $timeout(function() {
                if (scope.value === attrs.value) {
                    ngModel.$setViewValue(scope.value);
                }
            });
        }
    }
}]);

ちなみに、$timeoutが何を意味しているかはわからない(`・ω・’)

www.buildinsider.net

なるほど。描画の後に動作しているっぽいということね。(本当かな。。。)

<form>でonsubmit="return false;"した時の発火させるevent内では、$('form').removeAttr('onsubmit')だけで良い

知らんかったというか、これまでずっと下記コードを書いてた。

$('#button').on({
  click: function(e) {
    e.preventDefault(); // 一旦クリックされたイベントをキャンセル
    $(form).removeAttr('onsubmit').submit(); // 消した後に指定のformをsubmitする
  }
});

これでも一応できる。

が、例えば、submitするボタンが複数増えて、さらに押されたボタンを判断して色々処理させたい場合、これでは押されたボタンのnameとvalueが飛んでくれない。

押されたボタンがキャンセルされ、$('form')...submit()で、フォームをただsubmitしているだけなので、値が飛ばない。

なので、こうしましょう。

// 複数押されてもいいようにイベントを監視するボタンはクラス(css)にする
$('.button').on({
  click : function(e) {
    // e.preventDefault(); これをやると押されたsubmitボタンがわからなくなる
    $('#form').removeAttr('onsubmit'); // これをやらないとformを送信できない
    // $(this).submit(); // 最後に押されたところでsubmitする・・・ってわけでもなかった。。。
  }
});

これでおk

JavaScriptのnullとundefinedとは?

AngularJS触っていると、当たり前だけどJSも触る。んで、この辺勉強不足なので、調べたことメモ。

nullとは?

言わずともわかるプログラミング言語でみんながチェックするのに苦労する値。

動的言語は値が取れずブラウザ画面を真っ白にしたり、Javaではコンパイル後の動作確認でエラーを吐いてプロセス停止したりと散々。

これがJavaScriptにも実装されています。

Google Developer Toolのコンソールで確認するとこんな感じ。

> null
null

> typeof null
"object"

JSのnullは「オブジェクトが存在しない」とうことを表す特別な「オブジェクト」です。他の言語同様、単純に「値がない」という認識でもおkだと思う。

条件分岐ではnullfalseに自動変換される。しかし、論理値の比較ではfalseではない。

> if (! null) { console.log('false!'); }
false!

> null == false
false

また、nullをチェックするにはnullで比較すればおk

> var test = null
> test === null
true

undefined

defineは「定義するという意味」。undefinedは「未定義」ということだ。

JSでは初期化されていない値、存在していないオブジェクトやプロパティ、配列の要素を読みだしたときの値が未定義にあたります。んで、エラーになると。

> test // 定義していない変数を呼び出す
Uncaught ReferenceError: test is not defined(…)

> new TestObject() // 定義していないクラスをインスタンス化する
Uncaught ReferenceError: TestObject is not defined(…)

> TestObject = function() { console.log(v); } // 関数を用意。しかし、変数`v`は未定義
> var testObj = new TestObject(); // インスタンス化
Uncaught ReferenceError: v is not defined(…)TestObject

> TestObject = function(v) { console.log(v); } // 関数に引数を用意
> var testObj = new TestObject(); // インスタンス化。だけど、引数無し
undefined // エラーは発生せず、`undefined`が表示される

undefinedの判定はundefinedで確認しましょう。

> undefined == undefined
true

> undefined === undefined
true

> undefined == 'undefined'
false

> undefined === 'undefined'
false

文字列は同じじゃないようだ( ´ー`)フゥ

nullとundefinedの比較

どうやら「値がない」と「未定義」は同じっぽい。

しかし、型チェックでやると違う型と認識されるため、やっぱりそれぞれで比較したほうが良い。

> null === undefined
false

> null == undefined
true

まとめ

undefinedは予期せぬエラーが発生したという認識を持つべき。nullはプログラムレベルで予定通りの値が入ったという認識を持ってよい。なので、関数に「値がない」という値を渡す場合はnullを使いましょう。

テストコードは怖くない

短文。

これまでテストコードを避けがちでした。

コード書いたあとに、手動テストして、テストコード書いて。。。って流れが面倒だったので。

だけど、コード量が多くなるに連れて、さすがに手動のほうがだるくなってきたので、テストコード書かねば。ってなった。

でもめんどい。

ひとつの成約をかせた。と言っても単純な話。

1.コードを書く 2.手動テストする 3.テストコードを用意

っていう流れから、2だけ削除

1.コードを書く 2.テストコードを用意(動作を確認してテストする)

って、流れにした。

するとですよ。何故かうまく回っている感じ。もちろんUnitツールの使い方がわからないとすごく時間がかかるが、テストって何度も実行する&記述する頻度も多くなるので、汎用的なものはすぐに覚えられるはず。

また、コードとして残るので、実質効率化が進んでいる。すばらしい。

仕様変わった時に、テストコードも修正しないと行けないのがめんどいけど、やらないよりはマシだな。

Fixture作成するのが鬼門だけど。。。(^ω^;