Sự kiện thay đổi ngày tháng của giao diện người dùng jQuery không được KnockoutJS bắt gặp


134

Tôi đang cố gắng sử dụng KnockoutJS với jQuery UI. Tôi có một yếu tố đầu vào với một datepicker đính kèm. Tôi hiện đang chạy knockout.debug.1.2.1.jsvà có vẻ như sự kiện thay đổi không bao giờ bị Knockout bắt. Phần tử trông như thế này:

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

Tôi thậm chí đã thử thay đổi valueUpdateloại sự kiện nhưng vô ích. Có vẻ như Chrome gây ra focussự kiện ngay trước khi nó thay đổi giá trị, nhưng IE thì không.

Có một số phương pháp Knockout "rebinds tất cả các ràng buộc"? Về mặt kỹ thuật tôi chỉ cần thay đổi giá trị trước khi gửi lại cho máy chủ. Vì vậy, tôi có thể sống với cách giải quyết đó.

Tôi nghĩ vấn đề là lỗi của người hẹn hò, nhưng tôi không thể tìm ra cách khắc phục vấn đề này.

Có ý kiến ​​gì không?

Câu trả lời:


253

Tôi nghĩ rằng đối với trình hẹn giờ của giao diện người dùng jQuery, tốt hơn là sử dụng một ràng buộc tùy chỉnh sẽ đọc / ghi với các đối tượng Date bằng cách sử dụng các API được cung cấp bởi bộ đo thời gian.

Các ràng buộc có thể trông giống như (từ câu trả lời của tôi ở đây ):

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

Bạn sẽ sử dụng nó như:

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

Mẫu trong jsFiddle tại đây: http://jsfiddle.net/rniemeyer/NAgNV/


21
Điều tôi yêu là bạn đã không cắt góc trong cuốn sổ này, giống như với cuộc gọi lại bị loại bỏ. Một ví dụ âm thanh để làm theo trên con đường làm chủ KnockoutJS!
Dav

2
Và những gì về datepicker liên kết với một yếu tố được tạo ra một cách ngẫu nhiên ... ý tôi là, datepicker với một trình xử lý trực tiếp.
Phoenix_uy

6
Phoenix_uy: Để công cụ hẹn hò hoạt động với các đối tượng được tạo động, đảm bảo không đặt ID hoặc Tên của đầu vào.
James Reategui

1
Tôi đang sử dụng cái này và nó đang hoạt động hoàn hảo ngoại trừ một điều nhỏ - Nếu tôi đặt minDate hoặc maxDate bằng với một thứ có thể quan sát được thì nó sẽ không được cập nhật nếu thay đổi có thể quan sát được (ví dụ: nếu tôi có hai bộ đếm ngày trong đó ngày tối đa của đầu tiên là giá trị của lần thứ hai, nếu tôi cập nhật lần thứ hai thì nó không cập nhật ngày tối đa của lần đầu tiên) giống như câu hỏi này stackoverflow.com/questions/14732204/ Lỗi
PW Kad

2
Có vẻ như tên sự kiện là sai, ko.utils.registerEventHandler (phần tử, "changeDate", function () - nên là ko.utils.registerEventHandler (phần tử, "thay đổi", hàm ()
Adam Bilinski

13

Đây là phiên bản câu trả lời của RP Niemeyer sẽ hoạt động với các tập lệnh xác thực loại trực tiếp được tìm thấy ở đây: 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);
        }
    }
};

Các thay đổi là để xử lý sự kiện thay đổi để chuyển giá trị đã nhập và không phải là ngày cho các tập lệnh xác thực trước, sau đó chỉ đặt ngày thành có thể quan sát được nếu nó hợp lệ. Tôi cũng đã thêm xác thựcCore.init cần thiết cho các ràng buộc tùy chỉnh được thảo luận ở đây:

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

Tôi cũng đã thêm đề xuất của rpenrose về một sự thay đổi mờ nhạt để loại bỏ một số tình huống hẹn hò phiền phức gây cản trở mọi thứ.


2
Dường như không hoạt động đối với tôi, tôi nhận TypeError: obsable.isModified không phải là một chức năng trên dòng 313 của knockout.validation.js. Ví dụ nhỏ ở đây: frikod.se/~capitol/fel/test.html
Alexander Kjäll

Dòng quan trọng để làm cho nó hoạt động với thư viện xác nhận là: ko.bindingHandlers.validationCore.init (phần tử, valueAccessor, allBindingsAccessor);
CRice

11

Tôi đã sử dụng một cách tiếp cận khác. Vì knoutout.js dường như không kích hoạt sự kiện khi thay đổi, tôi đã buộc công cụ hẹn hò gọi thay đổi () cho đầu vào của nó sau khi đóng.

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

1
$ ('. datepicker'). datepicker ({onSelect: function (dateText) {$ ("# date_in"). trigger ("thay đổi");}});
elsadek

9

Mặc dù tất cả những câu trả lời này đã tiết kiệm cho tôi rất nhiều công việc, nhưng không có câu trả lời nào hoàn toàn phù hợp với tôi. Sau khi chọn một ngày, giá trị ràng buộc sẽ không cập nhật. Tôi chỉ có thể cập nhật nó khi thay đổi giá trị ngày bằng bàn phím sau đó nhấp vào hộp nhập. Tôi đã sửa lỗi này bằng cách tăng mã RP Niemeyer bằng mã của syb để có được:

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

Tôi nghi ngờ việc đặt ($ (phần tử) .datepicker ("getDate")); tuyên bố trong chức năng riêng của mình và đăng ký với tùy chọn.onSelect đã lừa?


2
Cảm ơn rất nhiều! Tôi đã thử mọi ví dụ và sau đó tìm thấy cái này ở cuối trang và cuối cùng nó cũng hoạt động. Tôi chỉ có một tinh chỉnh nhỏ để giá trị ràng buộc giữ nguyên định dạng "thân thiện với máy chủ" mà nó xuất hiện. Trong hàm funcOnSelectdate của bạn, hãy sử dụng: có thể quan sát được , $ (phần tử) .datepicker ('getDate')));
BrutalDev

Tôi nghĩ rằng nếu bạn ghi đè onSelectchức năng, nó sẽ không gây ra changesự kiện ...
NickL

6

Cảm ơn cho bài viết này tôi thấy nó rất hữu ích.

Nếu bạn muốn DatePicker hoạt động chính xác như hành vi mặc định của giao diện người dùng JQuery, tôi khuyên bạn nên thêm một phần mờ vào phần tử trong trình xử lý sự kiện thay đổi:

I E

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

        $(element).blur();

    });

Câu trả lời này không hoàn chỉnh? Đây có phải là nhận xét về câu trả lời của @ RPNiemeyer hay của người khác không?
rjmunro

3

Tôi đã giải quyết vấn đề này bằng cách thay đổi thứ tự của các tệp script bao gồ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>

Có các vấn đề tương tự với mô hình không được cập nhật mặc dù đầu vào hiển thị ngày được chọn chính xác từ công cụ hẹn hò. Bắt đầu xuống danh sách gợi ý .. nhưng .. đây chắc chắn là vấn đề của tôi. Hmmm .. dự án MVC của tôi đã có tập lệnh KO trước tập lệnh UI jquery và jquery trong một thời gian dài - sẽ phải kiểm tra kỹ lưỡng.
bkwdesign

2

Tương tự như RP Niemeyer, nhưng hỗ trợ tốt hơn cho WCF DateTime, Timezones và Sử dụng thuộc tính 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);
            }
        }
    };

Thưởng thức :)

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


1

Tôi nghĩ rằng nó có thể được thực hiện dễ dàng hơn nhiều: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

Vì vậy, bạn không cần xử lý thay đổi thủ công trong chức năng init.

Nhưng trong trường hợp này, biến 'myDate' của bạn sẽ chỉ nhận được giá trị hiển thị, không phải đối tượng Ngày.


1

Ngoài ra, bạn có thể chỉ định điều này trong ràng buộc:

Cập nhật:

 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
Điều này khắc phục sự cố khi giá trị ngày trả về ở định dạng chuỗi tức là. "2013-01-20T05: 00: 00" thay vì đối tượng ngày. Tôi gặp phải điều này khi tải dữ liệu từ API Web.
James Reategui

0

Dựa trên giải pháp của Ryan, myDate trả về chuỗi ngày tiêu chuẩn, đây không phải là chuỗi ngày lý tưởng trong trường hợp của tôi. Tôi đã sử dụng date.js để phân tích giá trị để nó sẽ luôn trả về định dạng ngày bạn muốn. Hãy xem ví dụ này fiddle Ví dụ .

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

Tôi cần cập nhật liên tục dữ liệu của mình từ máy chủ đã gặp phải vấn đề này nhưng không hoàn thành công việc cho việc chia sẻ nhu cầu của tôi bên dưới (định dạng ngày / Ngày của tôi (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();                   
        });
    };
}

Sau khi mô hình được định dạng chính xác cho đầu ra, tôi đã thêm một mẫu với tài liệu knoutjs :

<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

Rất ít người yêu cầu các tùy chọn datepicker năng động. Trong trường hợp của tôi, tôi cần một phạm vi ngày động - vì vậy đầu vào đầu tiên xác định giá trị tối thiểu của giây và giây đặt giá trị tối đa cho đầu tiên. Tôi đã giải quyết nó bằng cách mở rộng trình xử lý RP Niemeyer. Vì vậy, với bản gốc của mình:

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

Tôi đã thêm hai trình xử lý tương ứng với các tùy chọn tôi muốn sửa đổi:

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

Và sử dụng chúng như thế trong mẫu của tôi:

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

0

Sử dụng các ràng buộc tùy chỉnh được cung cấp trong các câu trả lời trước không phải lúc nào cũng có thể. Gọi điện $(element).datepicker(...)cần một số thời gian đáng kể và ví dụ, nếu bạn có vài chục hoặc thậm chí hàng trăm yếu tố để gọi phương thức này, bạn phải thực hiện "lười biếng", tức là theo yêu cầu.

Ví dụ: mô hình khung nhìn có thể được khởi tạo, inputs được chèn vào DOM, nhưng các bộ đếm ngày tương ứng sẽ chỉ được khởi tạo khi người dùng nhấp vào chúng.

Vì vậy, đây là giải pháp của tôi:

Thêm một ràng buộc tùy chỉnh cho phép đính kèm dữ liệu tùy ý vào một nút:

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

Sử dụng ràng buộc để attcah có thể quan sát được sử dụng cho inputgiá trị của:

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

Và cuối cùng, khi khởi tạo công cụ hẹn hò, hãy sử dụng onSelecttùy chọn này:

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

Bằng cách này, mỗi khi người dùng thay đổi ngày bằng công cụ hẹn hò, Knockout tương ứng cũng có thể được cập nhật.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.