jQuery UIの日付ピッカー変更イベントがKnockoutJSでキャッチされない


134

KnockoutJSをjQuery UIで使用しようとしています。日付ピッカーが添付されたinput要素があります。私は現在実行中ですknockout.debug.1.2.1.jsが、変更イベントがKnockoutによってキャッチされていないようです。要素は次のようになります。

<input type="text" class="date" data-bind="value: RedemptionExpiration"/>

valueUpdateイベントの種類を変更しようとしても失敗しました。Chrome focusは値を変更する直前にイベントを発生させるようですが、IEは発生させません。

「すべてのバインディングを再バインドする」Knockoutメソッドはありますか?技術的には、値を変更してからサーバーに送信する必要があります。だから私はそのような回避策で生きることができました。

問題は日付ピッカーのせいだと思いますが、これを修正する方法がわかりません。

何か案は?

回答:


253

jQuery UI datepickerの場合、datepickerによって提供されるAPIを使用してDateオブジェクトで読み取り/書き込みを行うカスタムバインディングを使用することをお勧めします。

バインディングは次のようになります(ここでの私の回答から):

ko.bindingHandlers.datepicker = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {},
            $el = $(element);

        $el.datepicker(options);

        //handle the field changing by registering datepicker's changeDate event
        ko.utils.registerEventHandler(element, "changeDate", function () {
            var observable = valueAccessor();
            observable($el.datepicker("getDate"));
        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $el.datepicker("destroy");
        });

    },
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            $el = $(element);

        //handle date data coming via json from Microsoft
        if (String(value).indexOf('/Date(') == 0) {
            value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
        }

        var current = $el.datepicker("getDate");

        if (value - current !== 0) {
            $el.datepicker("setDate", value);
        }
    }
};

次のように使用します。

<input data-bind="datepicker: myDate, datepickerOptions: { minDate: new Date() }" />

jsFiddleのサンプルはこちら:http ://jsfiddle.net/rniemeyer/NAgNV/


21
私が気に入っているのは、このコールバックのように、このバインダーで手抜きをしなかったことです。KnockoutJSを習得するための道のりをたどるサウンドの例!
Dav

2
そして、動的に作成される要素にバインドされたdatepickerについてはどうでしょう...つまり、ライブハンドラーを備えたdatepickerです。
Phoenix_uy 2012

6
Phoenix_uy:日付ピッカーが動的に作成されたオブジェクトを処理するために、入力のIDまたは名前を設定しないでください。
James Reategui 2013年

1
私はこれを使用していて、小さなことを除いて完全に機能しています-minDateまたはmaxDateをオブザーバブルに設定した場合、そのオブザーバブルが変更されても更新されません(たとえば、2つの日付ピッカーがあり、最初は2番目の値です。2番目を更新しても、最初の最大日付は更新されません)この質問と同じです。stackoverflow.com
PW Kad

2
イベント名が間違っているように見えます、ko.utils.registerEventHandler(element、 "changeDate"、function()-ko.utils.registerEventHandler(element、 "change"、function()である必要があります
Adam Bilinski

13

ここにあるノックアウト検証スクリプトで動作するRP Niemeyerの回答のバージョンを次に示します。http//github.com/ericmbarnard/Knockout-Validation

ko.bindingHandlers.datepicker = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {};
        $(element).datepicker(options);

        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();
            observable($(element).val());
            if (observable.isValid()) {
                observable($(element).datepicker("getDate"));

                $(element).blur();
            }
        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).datepicker("destroy");
        });

        ko.bindingHandlers.validationCore.init(element, valueAccessor, allBindingsAccessor);

    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());

        //handle date data coming via json from Microsoft
        if (String(value).indexOf('/Date(') == 0) {
            value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
        }

        current = $(element).datepicker("getDate");

        if (value - current !== 0) {
            $(element).datepicker("setDate", value);
        }
    }
};

変更は、最初に検証スクリプトに日付ではなく入力された値を渡すための変更イベントハンドラーに対して行われ、有効な場合にのみ日付をオブザーバブルに設定します。ここで説明するカスタムバインディングに必要なvalidationCore.initも追加しました。

http://github.com/ericmbarnard/Knockout-Validation/issues/69

また、厄介な日付ピッカーのシナリオが邪魔にならないようにするために、変更をぼかすというrpenroseの提案を追加しました。


2
TypeErrorが発生します。observable.isModifiedは、knockout.validation.jsの313行目の関数ではありません。ここの小さな例:frikod.se/~capitol/fel/test.html
AlexanderKjäll2013

検証ライブラリで機能させるための重要な行は次のとおりです。ko.bindingHandlers.validationCore.init(element、valueAccessor、allBindingsAccessor);
CRice

11

私は別のアプローチを使用しました。knockout.jsは変更時にイベントを発生させないように見えるので、閉じられた入力に対してdatepickerがchange()を呼び出すように強制しました。

$(".date").datepicker({
    onClose: function() {
        $(this).change(); // Forces re-validation
    }
});

1
$( '。datepicker')。datepicker({onSelect:function(dateText){$( "#date_in")。trigger( "change");}});
elsadek 2014

9

これらの答えはすべて私に多くの仕事を節約しましたが、それらのどれもが私のために十分に機能しませんでした。日付を選択した後、バインドされた値は更新されません。キーボードを使用して日付の値を変更し、入力ボックスの外をクリックしたときにのみ、更新を取得できました。RP Niemeyerのコードにsybのコードを追加してこれを修正し、以下を取得しました。

ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            //initialize datepicker with some optional options
            var options = allBindingsAccessor().datepickerOptions || {};

            var funcOnSelectdate = function () {
                var observable = valueAccessor();
                observable($(element).datepicker("getDate"));
            }

            options.onSelect = funcOnSelectdate;

            $(element).datepicker(options);

            //handle the field changing
            ko.utils.registerEventHandler(element, "change", funcOnSelectdate);

            //handle disposal (if KO removes by the template binding)
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                $(element).datepicker("destroy");
            });

        },
        update: function (element, valueAccessor) {

            var value = ko.utils.unwrapObservable(valueAccessor());
            if (typeof(value) === "string") { // JSON string from server
                value = value.split("T")[0]; // Removes time
            }

            var current = $(element).datepicker("getDate");

            if (value - current !== 0) {
                var parsedDate = $.datepicker.parseDate('yy-mm-dd', value);
                $(element).datepicker("setDate", parsedDate);
            }
        }
    };

observable($(element).datepicker( "getDate"));を置くと思います。独自の関数でステートメントを作成し、options.onSelectに登録してトリックを実行しましたか?


2
どうもありがとう!私はすべての例を試しましたが、ページの下部にあるこの例を見つけたので、ようやく動作しました。バインドされた値がダウンしたときと同じ「サーバーフレンドリー」形式のままになるように、私は少し微調整しました。funcOnSelectdate関数でこれを使用します:observable($。datepicker.formatDate( 'yy-mm-dd' 、$(element).datepicker( 'getDate')));
BrutalDev 2013年

onSelect関数をオーバーライドしてもchangeイベントは発生しないと思います...
NickL

6

この記事をありがとう、私はそれがとても役に立ったと思った。

DatePickerをJQuery UIのデフォルトの動作とまったく同じように動作させたい場合は、変更イベントハンドラーの要素にブラーを追加することをお勧めします。

すなわち

    //handle the field changing
    ko.utils.registerEventHandler(element, "change", function () {
        var observable = valueAccessor();
        observable($(element).datepicker("getDate"));

        $(element).blur();

    });

この答えは完全に見えませんか?これは@RPNiemeyerの回答に対するコメントですか、それとも他の誰かのコメントですか?
rjmunro

3

含まれているスクリプトファイルの順序を変更することで、この問題を解決しました。

<script src="@Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script>
<script src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>

入力が日付ピッカーから正しく選択された日付をレンダリングしてもモデルが更新されないという同様の問題がありました。提案のリストを書き始めましたが、これは間違いなく私の問題でした。うーん..私のMVCプロジェクトでは、jqueryおよびjquery UIスクリプトよりも前にKOスクリプトがありました-徹底的にテストする必要があります。
bkwdesign 2014年

2

RP Niemeyerと同じですが、WCF DateTime、Timezones、およびDatePicker onSelect JQueryプロパティの使用のサポートが向上しています。

        ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            //initialize datepicker with some optional options
            var options = allBindingsAccessor().datepickerOptions || {};

            var funcOnSelectdate = function () {
                var observable = valueAccessor();
                var d = $(element).datepicker("getDate");
                var timeInTicks = d.getTime() + (-1 * (d.getTimezoneOffset() * 60 * 1000));

                observable("/Date(" + timeInTicks + ")/");
            }
            options.onSelect = funcOnSelectdate;

            $(element).datepicker(options);

            //handle the field changing
            ko.utils.registerEventHandler(element, "change", funcOnSelectdate);

            //handle disposal (if KO removes by the template binding)
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                $(element).datepicker("destroy");
            });

        },
        update: function (element, valueAccessor) {
            var value = ko.utils.unwrapObservable(valueAccessor());

            //handle date data coming via json from Microsoft
            if (String(value).indexOf('/Date(') == 0) {
                value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
            }

            current = $(element).datepicker("getDate");

            if (value - current !== 0) {
                $(element).datepicker("setDate", value);
            }
        }
    };

楽しい :)

http://jsfiddle.net/yechezkelbr/nUdYH/


1

私はそれがはるかに簡単にできると思います: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

したがって、init関数で手動の変更処理を行う必要はありません。

ただし、この場合、「myDate」変数は表示される値のみを取得し、Dateオブジェクトは取得しません。


1

または、バインディングでこれを指定できます。

更新:

 function (element, valueAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor()),
        current = $(element).datepicker("getDate");

    if (typeof value === "string") {            
       var dateValue = new Date(value);
       if (dateValue - current !== 0)
           $(element).datepicker("setDate", dateValue);
    }               
}

2
これにより、返された日付値が文字列形式の場合の問題が修正されます。日付オブジェクトの代わりに「2013-01-20T05:00:00」。Web APIからデータをロードするときに私はこれに遭遇しました。
James Reategui 2013年

0

Ryanのソリューションに基づいて、myDateは標準の日付文字列を返しますが、これは私の場合は理想的なものではありません。date.jsを使用して値を解析したため、常に必要な日付形式が返されます。この例のフィドルの例を見てください。

update: function(element, valueAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor()),
        current = $(element).datepicker("getDate");
    var d = Date.parse(value);
    if (value - current !== 0) {
        $(element).datepicker("setDate", d.toString("MM/dd/yyyy"));   
    }
}

0

私はサーバーからデータを繰り返し更新する必要がありましたが、これに遭遇しましたが、以下で共有する必要があるため、ジョブを完了しませんでした(私の日付形式/ Date(1224043200000)/):

//Object Model
function Document(data) {
        if (String(data.RedemptionExpiration).indexOf('/Date(') == 0) {
            var newDate = new Date(parseInt(data.BDate.replace(/\/Date\((.*?)\)\//gi, "$1")));
            data.RedemptionExpiration = (newDate.getMonth()+1) +
                "/" + newDate.getDate() +
                "/" + newDate.getFullYear();
        }
        this.RedemptionExpiration = ko.observable(data.RedemptionExpiration);
}
//View Model
function DocumentViewModel(){
    ///additional code removed
    self.afterRenderLogic = function (elements) {
        $("#documentsContainer .datepicker").each(function () {
            $(this).datepicker();                   
        });
    };
}

モデルが出力用に正しくフォーマットされた後、ドキュメントknockoutjsを含むテンプレートを追加しました:

<div id="documentsContainer">
    <div data-bind="template: { name: 'document-template', foreach: documents, afterRender: afterRenderLogic }, visible: documents().length > 0"></div>
</div>

//Inline template
<script type="text/html" id="document-template">
  <input data-bind="value: RedemptionExpiration" class="datepicker" />
</script>

0

動的な日付ピッカーオプションを求める人はほとんどいません。私の場合、動的な日付範囲が必要でした。最初の入力は2番目の最小値を定義し、2番目は最初の最大値を設定します。RP Niemeyerのハンドラーを拡張して解決しました。だから彼のオリジナルに:

ko.bindingHandlers.datepicker = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {},
            $el = $(element);

        $el.datepicker(options);

        //handle the field changing by registering datepicker's changeDate event
        ko.utils.registerEventHandler(element, "change", function() {
            var observable = valueAccessor();
            observable($el.datepicker("getDate"));
        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $el.datepicker("destroy");
        });

    },
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            $el = $(element);

        //handle date data coming via json from Microsoft
        if (String(value).indexOf('/Date(') == 0) {
            value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
        }

        var current = $el.datepicker("getDate");

        if (value - current !== 0) {
            $el.datepicker("setDate", value);
        }
    }
};

変更したいオプションに対応するハンドラーをさらに2つ追加しました。

ko.bindingHandlers.minDate = {
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            current = $(element).datepicker("option", "minDate", value);
    }
};

ko.bindingHandlers.maxDate = {
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            current = $(element).datepicker("option", "maxDate", value);
    }
};

そして、私のテンプレートでそれらをそのように使用しました:

<input data-bind="datepicker: selectedLowerValue, datepickerOptions: { minDate: minValue()}, maxDate: selectedUpperValue" />       
<input data-bind="datepicker: selectedUpperValue, datepickerOptions: { maxDate: maxValue()}, minDate: selectedLowerValue" />

0

以前の回答で提供されたカスタムバインディングを使用することが常に可能であるとは限りません。呼び出す$(element).datepicker(...)はかなりの時間がかかります。たとえば、このメソッドを呼び出すための要素が数十または数百にも及ぶ場合は、「遅延」、つまりオンデマンドで実行する必要があります。

たとえば、ビューモデルが初期化され、 inputされ、sはDOMに挿入されますが、対応する日付ピッカーはユーザーがクリックしたときにのみ初期化されます。

だから、ここに私の解決策があります:

ノードに任意のデータをアタッチできるカスタムバインディングを追加します。

KO.bindingHandlers.boundData = {
  init: function(element, __, allBindings) {
    element.boundData = allBindings.get('boundData');
  }
};

バインディングを使用して、inputの値に使用されるオブザーバブルを追加します。

<input type='text' class='my-date-input'
       data-bind='textInput: myObservable, boundData: myObservable' />

最後に、datepickerを初期化するときに、onSelectオプションを使用します。

$('.my-date-input').datepicker({
  onSelect: function(dateText) {
    this.myObservable(dateText);
  }
  //Other options
});

このように、ユーザーが日付ピッカーで日付を変更するたびに、対応するKnockoutオブザーバブルも更新されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.