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.