KnockoutJS에 의해 잡히지 않는 jQuery UI 날짜 선택기 변경 이벤트


134

jQuery UI에서 KnockoutJS를 사용하려고합니다. 날짜 선택기가 첨부 된 입력 요소가 있습니다. 현재 실행 중이며 knockout.debug.1.2.1.js변경 이벤트가 Knockout에 걸리지 않는 것 같습니다. 요소는 다음과 같습니다.

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

valueUpdate이벤트 유형 변경을 시도 했지만 아무 소용이 없습니다. Chrome이 focus값을 변경하기 직전 에 이벤트를 일으키는 것처럼 보이지만 IE는 그렇지 않습니다.

"모든 바인딩을 리 바인딩하는"녹아웃 방법이 있습니까? 기술적으로 서버로 다시 보내기 전에 변경된 값만 있으면됩니다. 그래서 나는 그런 종류의 해결 방법으로 살 수있었습니다.

문제는 datepicker의 잘못이라고 생각하지만이 문제를 해결하는 방법을 알 수 없습니다.

어떤 아이디어?

답변:


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
내가 좋아하는 것은 dispose 콜백과 같이이 바인더의 모서리를 자르지 않았다는 것입니다. KnockoutJS 숙달로가는 길을 따라가는 소리 예제!
— Dav

2
그리고 datepicker는 동적으로 생성 된 요소에 바인딩되었습니다 ... 즉, 라이브 핸들러가있는 datepicker를 의미합니다.
— Phoenix_uy

6
Phoenix_uy : 날짜 선택 도구가 동적으로 생성 된 개체와 작동하도록하려면 입력의 ID 또는 이름을 설정하지 마십시오.
— James Reategui

1
나는 이것을 사용하고 있으며 하나의 작은 것을 제외하고는 완벽하게 작동합니다-minDate 또는 maxDate를 observable과 동일하게 설정하면 observable이 변경되면 업데이트되지 않습니다 (예 : 최대 날짜가 2 개인 datepickers가있는 경우 첫 번째는 두 번째의 값입니다. 두 번째를 업데이트하면 첫 번째의 최대 날짜를 업데이트하지 않습니다.)이 질문과 동일합니다. stackoverflow.com/questions/14732204/…
— PW Kad

2
이벤트 이름이 잘못된 것 같습니다. ko.utils.registerEventHandler (element, "changeDate", function ()-ko.utils.registerEventHandler (element, "change", function ()
— 이어야

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
— Alexander Kjäll

유효성 검증 라이브러리를 사용하기위한 중요한 행은 다음과 같습니다. 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

9

이 모든 대답이 저에게 많은 작업을 절약 해 주었지만 그 중 어느 것도 나를 위해 완전히 효과가 없었습니다. 날짜를 선택한 후에는 바인드 된 값이 업데이트되지 않습니다. 키보드를 사용하여 날짜 값을 변경 한 다음 입력 상자를 클릭 할 때만 업데이트 할 수 있습니다. 나는 이것을 얻기 위해 syb의 코드로 RP Niemeyer의 코드를 보강하여 이것을 고쳤다.

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

나는 당신이 onSelect기능 을 무시하면 change이벤트 가 발생하지 않을 것이라고 생각합니다 ...
— NickL

6

이 기사에 감사드립니다. 매우 유용하다는 것을 알았습니다.

DatePicker가 JQuery UI 기본 동작과 똑같이 동작하도록하려면 change 이벤트 핸들러에서 요소에 흐림 효과를 추가하는 것이 좋습니다.

즉

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

        $(element).blur();

    });

이 답변이 완전하게 보이지 않습니까? 이것은 @RPNiemeyer의 답변 또는 다른 사람의 의견입니까?
— rjmunro 2016 년

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>

입력이 datepicker에서 올바르게 선택된 날짜를 렌더링했지만 모델이 업데이트되지 않는 비슷한 문제가 있습니다. 제안 목록을 시작했지만 ..... 이것은 분명히 내 문제였습니다. 흠 .. 내 MVC 프로젝트는 오랫동안 jquery 및 jquery UI 스크립트보다 KO 스크립트를 가지고 있습니다. 철저히 테스트해야합니다.
— bkwdesign

2

RP Niemeyer와 동일하지만 WCF DateTime, 시간대 및 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

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

동적 날짜 선택기 옵션을 요청한 사람은 거의 없습니다. 필자의 경우 동적 날짜 범위가 필요했기 때문에 첫 번째 입력은 두 번째의 최소값을 정의하고 두 번째 입력은 첫 번째의 최대 값을 설정합니다. 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);
        }
    }
};

수정하려는 옵션에 해당하는 두 개의 처리기를 추가했습니다.

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의 값에 사용 된 관찰 가능한 값 을 attcah하려면 바인딩을 사용하십시오 .

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

마지막으로 날짜 선택기를 초기화 할 때 onSelect옵션을 사용하십시오 .

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

이 방법으로 사용자가 날짜 선택기로 날짜를 변경할 때마다 해당 녹아웃 관찰 가능 항목도 업데이트됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.