KnockoutJS tarafından yakalanmayan jQuery UI datepicker değişiklik olayı


134

JQuery UI ile KnockoutJS kullanmaya çalışıyorum. Bir tarih seçici bağlı bir giriş öğesi var. Şu anda koşuyorum knockout.debug.1.2.1.jsve değişim olayı asla Nakavt tarafından yakalanmıyor gibi görünüyor. Öğe şöyle görünür:

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

Hatta valueUpdateolay türünü değiştirmeye çalıştım ama boşuna. Görünüşe göre Chrome, bir focusdeğeri değiştirmeden hemen önce bir etkinliğe neden oluyor , ancak IE değişmiyor.

"Tüm bağları yeniden hatırlatan" bir Nakavt yöntemi var mı? Teknik olarak sadece sunucuya geri göndermeden önce değiştirilen değere ihtiyacım var. Böylece bu tür bir çözümle yaşayabilirdim.

Bence problem datepicker'ın hatası, ama bunu nasıl düzeltebileceğimi bilemiyorum.

Herhangi bir fikir?

Yanıtlar:


253

JQuery UI datepicker için datepicker tarafından sağlanan API'leri kullanarak Date nesneleri ile okuyacak / yazacak özel bir bağlayıcı kullanmanın tercih olduğunu düşünüyorum.

Bağlanma şöyle görünebilir ( buradaki cevabımdan ):

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);
        }
    }
};

Bunu şöyle kullanırsınız:

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

JsFiddle'da örnek: http://jsfiddle.net/rniemeyer/NAgNV/


21
Sevdiğim şey, bu geri çağırmada olduğu gibi bu ciltte köşeleri kesmemeniz. KnockoutJS ustalığına giden yolda takip etmek için sağlam bir örnek!
Dav

2
Ve tarih seçici, dinamik olarak oluşturulmuş bir öğeye bağlandı ... yani, canlı bir işleyiciye sahip tarih seçici.
Phoenix_uy

6
Phoenix_uy: Tarih seçicinin dinamik olarak oluşturulmuş nesnelerle çalışması için, girdinin kimliğini veya Adını ayarlamadığınızdan emin olun.
James Reategui

1
Bunu kullanıyorum ve küçük bir şey dışında mükemmel çalışıyor - Eğer minDate veya maxDate'i gözlemlenebilir bir değere ayarlarsam, bu gözlemlenebilir değiştiğinde güncellenmez (ör. birincisi ikincinin değeridir, ikincisini güncellersem, ikincisinin maksimum tarihini güncellemez) bu soru ile aynı stackoverflow.com/questions/14732204/…
PW Kad

2
etkinlik adı yanlış gibi görünüyor, ko.utils.registerEventHandler (öğe, "changeDate", function () - ko.utils.registerEventHandler (element, "change", function ()
Adam Bilinski

13

RP Niemeyer'in cevabının burada bulunan nakavt doğrulama komut dosyalarıyla çalışacak bir versiyonu: 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);
        }
    }
};

Değişiklik, olay doğrulama işleyicisine, tarihi doğrulama komut dosyalarına tarihi değil, önce girilen değeri iletmek için yapılır, daha sonra yalnızca geçerliyse tarihi gözlemlenebilir duruma ayarlar. Ayrıca burada tartışılan özel bağlamalar için gerekli validationCore.init ekledim:

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

Ayrıca rpenrose'un bazı şeyleri engellemeye yönelik bazı sinir bozucu tarih seçici senaryolarını ortadan kaldırmak için değişiklik üzerinde bir bulanıklık önerisi ekledim.


2
Benim için çalışmıyor gibi görünüyor, TypeError: gözlemlenebilir.isModified knockout.validation.js satır 313 üzerinde bir işlev değildir. Buradaki küçük örnek: frikod.se/~capitol/fel/test.html
Alexander Kjäll

Doğrulama kitaplığıyla çalışmasını sağlamak için önemli satır: ko.bindingHandlers.validationCore.init (element, valueAccessor, allBindingsAccessor);
CRice

11

Farklı bir yaklaşım kullandım. Knockout.js olayı değişiklik sırasında tetiklemiyor gibi göründüğünden, tarih seçiciyi girdikten sonra change () öğesini çağırmaya zorladım.

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

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

9

Tüm bu cevaplar beni çok fazla işten kurtarsa ​​da, hiçbiri benim için tam olarak işe yaramadı. Bir tarih seçildikten sonra, bağlanan değer güncellenmez. Yalnızca klavyeyi kullanarak tarih değerini değiştirip giriş kutusunun dışına tıklarken güncellemeyi alabilirdim. Ben almak için syb kod ile RP Niemeyer kodunu artırarak bu düzeltildi:

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);
            }
        }
    };

Ben gözlemlenebilir ($ (element) .datepicker ("getDate")) koyarak şüpheleniyorum; kendi işlevinde ifade ve bunu options.onSelect ile kaydetme hile yaptı?


2
Milyonlarca kez teşekkürler! Her örneği denedim ve sonra bunu sayfanın altında buldum ve sonunda işe yaradı. Sınır değer aşağı geldi aynı "sunucu dostu" biçiminde kalır böylece benim sadece küçük bir tweak var. , $ (eleman) .datepicker ('getDate')));
BrutalDev

Bence onSelectişlevi geçersiz kılarsanız changeolayı yükseltmez ...
NickL

6

Bu yazı için teşekkürler çok yararlı buldum.

DatePicker tam olarak JQuery UI varsayılan davranış gibi davranmasını istiyorsanız, change olay işleyicisinde öğeye bir bulanıklık eklemenizi öneririz:

yani

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

        $(element).blur();

    });

Bu cevap tam görünmüyor mu? Bu @ RPNiemeyer'ın cevabı veya başka birinin cevabı hakkında bir yorum mu?
rjmunro

3

Dahil olan komut dosyalarının sırasını değiştirerek bu sorunu çözdüm:

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

Giriş tarih seçiciden doğru seçilen tarihi oluştursa bile, modelle ilgili benzer sorunlar yaşandı. Öneriler listesine başladı .. ama .. bu kesinlikle benim sorunumdu. Hmmm .. MVC projem uzun bir süre jquery ve jquery UI komut dosyalarının önünde KO betiği vardı - iyice test etmek zorunda kalacak.
bkwdesign

2

RP Niemeyer ile aynı, ancak WCF DateTime, Timezones ve DatePicker onSelect JQuery özelliğini kullanma daha iyi destek.

        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);
            }
        }
    };

Zevk almak :)

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


1

Bence çok daha kolay yapılabilir: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

Bu nedenle, init işlevinde manuel değişiklik işlemeye gerek yoktur.

Ancak bu durumda, 'myDate' değişkeniniz Date nesnesi değil yalnızca görünür değer alır.


1

Alternatif olarak, bunu bağlayıcı olarak belirtebilirsiniz:

Güncelleme:

 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
Bu, döndürülen tarih değeri dize biçiminde olduğunda yani sorunu giderir. Tarih nesnesi yerine "2013-01-20T05: 00: 00". Web API'sından veri yüklerken buna rastladım.
James Reategui

0

Ryan'ın çözümüne dayanarak, myDate benim durumumda ideal olmayan standart tarih dizesini döndürür. Değeri ayrıştırmak için date.js'yi kullandım, böylece her zaman istediğiniz tarih biçimini döndürür. Bu örnek keman örneğine bir göz atın .

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

Verilerimi sunucudan tekrar tekrar güncellemem gerekiyordu, ancak aşağıdaki paylaşım paylaşımım için işimi bitirmedim (tarih formatım / Tarih (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();                   
        });
    };
}

Model çıktı için doğru biçimlendirildikten sonra knockoutjs belgelerine sahip bir şablon ekledim :

<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

Birkaç kişi dinamik tarih seçici seçenekleri istedi. Benim durumumda dinamik bir tarih aralığına ihtiyacım vardı - bu yüzden ilk giriş ikincinin min değerini tanımlar ve ikincisi birincinin maksimum değerini ayarlar. RP Niemeyer'in işleyicisini uzatarak çözdüm. Orijinaline göre:

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);
        }
    }
};

Değiştirmek istediğim seçeneklere karşılık gelen iki işleyici daha ekledim:

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);
    }
};

Ve bunları şablonumda böyle kullandım:

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

0

Önceki yanıtlarda verilen özel ciltleri kullanmak her zaman mümkün değildir. Arama $(element).datepicker(...)biraz zaman alır ve örneğin, bu yöntemi çağıracak birkaç düzine hatta yüzlerce elemana sahipseniz, bunu "tembel", yani talep üzerine yapmanız gerekir.

Örneğin, görünüm modeli başlatılabilir, inputbunlar DOM'a eklenir, ancak karşılık gelen tarih seçiciler yalnızca bir kullanıcı tıklattığında başlatılır.

İşte benim çözümüm:

Bir düğüme rasgele veri eklenmesine izin veren özel bir bağlayıcı ekleyin:

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

Bağlanmayı, değeri için kullanılan gözlemlenebilir olanı atfetmek için kullanın input:

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

Ve son olarak, tarih seçiciyi başlatırken bu onSelectseçeneği kullanın:

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

Bu şekilde, bir kullanıcı tarih seçici ile tarihi her değiştirdiğinde, karşılık gelen Nakavt gözlemlemesi de güncellenir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.