Compare observable dates with knockout and jQuery Validation - javascript

Is it possible to compare dates with Knockout observables and jQuery Validation? I have a StartDate and an EndDate, both observables. I want to validate that the EndDate is always greater than the StartDate.
I've tried the addMethod feature but I can't seem to pass in self.StartDate. It doesn't seem that the addMethod accepts KO observables, so I have to pass in the string ID, "#StartDate".
$.validator.addMethod("dateGreater", function(value, element, params){
var endDate = new Date(value); // this returns incorrect date.
var startDate = new Date($(params).val());
return this.optional(element) || startDate < endDate;
}, "The End Date must be greater thatn the Start Date.");
The problem with this code is that when the user selects a date with the DatePicker, the EndDate returns the previous date, not the currently selected date, and since the StartDate is not an observable being validated, if the user changes it, the validation doesn't fire.
I guess my question really is, can this be done with jQuery Validation, or should I be using Knockout-validation?
I created a simple jsfiddle that shows what I'm trying to accomplish. http://jsfiddle.net/kahanu/htqfa1qw/6/

This answer follows the approach of keeping jquery-validation outside of KO. I think this is OK, because you don't lose any magic; jquery-validation has its own framework for tracking changes and it keeps KO and jquery-validation separate, which is simple.
Here is your fiddle, with this approach applied:
http://jsfiddle.net/htqfa1qw/7/
The fixes were:
jquery-validation requires names in the input elements
These names then correspond with the keys in the rules object.
Param passing is weird and the docs don't explain it well!
So your rules object now looks like this:
EndDate: {
required: true,
dateGreater: {
param: "StartDate"
}
}
which is saying we want the <input name="EndDate"> to be greater than the <input name="StartDate">
We then modify the rule so that it gets the value directly from the DOM without needing to go via KO:
var start = new Date($("[name="+param+"]").val());
If you did want to do it via KO by passing in the observable as you describe, then you could do this:
http://jsfiddle.net/htqfa1qw/9/
Instantiate your data model before applying it:
var dm = new DataModel();
ko.applyBindings(dm);
Then alter the validation rule to lookup the method by the param, and call it:
$.validator.addMethod("dateGreater", function (endDate, element, param) {
var end= new Date(endDate);
var start = new Date(dm[param].call());
return this.optional(element) || start < end;
}, "Start Date must be less than End Date.");
(With ref to the comments) Once it is working with the bootstrap-datetime widget, you need to be careful about how the widget changes the input value, and how it's method of changing the value ineracts with both the jquery-validation framework and also the KO framework, both of which rely on change events.
In your fiddle the problem seems to be that jquery-validation gets triggered with the old date value before the widget successfully loads the new value into the input. Hence you see validation happening on the old value... The quick fix seems to be to call jquery-validation's valid() method, to trigger a manual validation, in the event handler you already have:
$el.datepicker(options).on("changeDate", function (ev) {
var observable = valueAccessor();
observable(ev.date);
$(this).datepicker('hide');
// Poke jquery validation to validate this new value.
$("#date-form").valid();
});
Demo here:
http://jsfiddle.net/htqfa1qw/12/
In this fiddle there's some console.logs and you can track the sequence:
validation is called with the old value (the "Start date..." log statement)
your event handler kicks in with the new value (the "changeDate" log statement)
We manually call valid() and validation is called with the new value (the 2nd "Start date..." log statement)

Related

Knockout and Kendo Datetimepicker resets date to today when changing time

I have page that uses the Kendo DateTimePicker with the input bound to a Knockout Observable. I am using knockout-kendo.js for the bridging.
As you can see in this JSFiddle, I set up the observable with today's date time at initialization. I later set the value (as if it was loaded from an ajax retrieval, I just didn't want to deal with the echo api on JSFiddle)
var SchedulerAppointmentModel = function () {
var self = this,
saved = [],
initComplete = false;
self.StartDateTime = ko.observable(moment().format("MM/DD/YYYY hh:mm A"));
self.StartDateTime(moment("10/23/2014 1:30 PM").format("MM/DD/YYYY h:mm A"));
};
The problem is, if I change the time in my drop down box, the date resets to today. I wanted to leave the date alone.
I think it has something to do with setting the kendoDateTimePicker in code and not in html. I think if i could somehow set the max later, it would work.
Not sure if you still need an answer, but...
I see you've pulled in the knockout-kendo library. With that there is no need to manually initialize the dateTimePicker. You can simply use the binding that comes with that library:
<input id="appt-start-datetime" value="0" data-bind="kendoDateTimePicker: StartDateTime" />
Fiddle Updated

Can you make an Ember.TextField wait until change to update its value binding?

I extended an Ember.TextField to include a date picker. A function that observes the text field’s value attempts to parse the string in the text field and update a date property. This is all fine and good when you use the date picker, but if you were to try to type a date into the box, it goes crazy because the value gets updated on every keydown (or keyup or whatever Ember’s default event to update the value bindings for a TextField), and it immediately re-updates the value of the text field with the nicely-formatted date string that came from what it just parsed. Example:
Input says 10/26/2014
You insert your cursor after 2014 and hit backspace
The value has changed, so a handler parses 10/26/201 and updates a date property
The date property has changed, so a handler formats the date as MM/d/yyyy and sets the value
The input now says 10/26/0201
Rather than changing the way those handlers work, all my problems would be solved if I could tell Ember to update the value binding when the input’s change event fires, rather than trying to update everything on every keystroke. I know this can be done in AngularJS and Knockout, but I can’t find anything about it for Ember.
EDIT
I know I can change the way my code works to avoid this specific problem. At this point, I’m more interested for purposes of edification, in a yes-or-no answer that specifically addresses the question that is the title of this post. I’m starting to think the answer is no, but wanted to poll the community.
I wrote a blog post that may offer some solutions about Date Pickers And Validation In Ember with examples here is one of the JSBins from the post.
Write your own extension of text field component and add the change callback.
App.DateTextComponent = Em.TextField.extend({
change: function(event){
var value = this.get('value');
// massage data
value += "foo";
this.set('value', value);
}
});
Example: http://emberjs.jsbin.com/suzami/2/edit
If you really want to get a call when the value changes after the fact, don't observe the value, use actions.
App.DateTextComponent = Em.TextField.extend({
change: function(event){
var value = this.get('value');
this.sendAction('changed', value);
}
});
{{date-text value=foo changed='fooChanged'}}
Example: http://emberjs.jsbin.com/suzami/3/edit?html,js,output

JavaScript to update End Date when Start Date is changed

For the site I am developing, I use a form to update the different fields of a model.
Some of those fields need to be updated according to the values of others.
For example, each instance of a Task, has a start date, an end date, and a length which have to verify the following : End = Start + Length (where weekends are not counted, but that is a secondary issue).
So one of the functions I am trying to write would do :
When the user changes the value of the input Start_date
Get that date from the input field
Get the value of the field Length
Update the value of the input "End date" with (Start_date + Length)
The three inputs concerned by my example have the following IDs : id_start, id_length, id_finish.
Here is what I have written, but which doesn't work at all (nothing visible happens...) :
<script>
$( "#id_start" ).change(function() {
var start = new Date();
var finish = new Date();
var numberOfDays = document.getElementById('id_length').value;
start = document.getElementById('id_start').value;
finish.setdate(start.getDate()+document.getElementById('id_length'))
$('#id_finish').val(finish);
});
</script>
Any hint at a solution to make it work would be hugely appreciated.
Let's look at what your code is actually doing.
var start = new Date();
Here you create a new Date object.
start = document.getElementById('id_start').value;
Here, you change the value of start to the value of the DOM element with the id = 'id_start'. The problem is, this value isn't a Date object. Even the new HTML Date input type (which isn't supported by Firefox or IE) will only give you a string when you pull it's value. So you've created a new Date object, and then you throw it out immediately.
So now start isn't a Date, but a string. In this line:
finish.setdate(start.getDate()+document.getElementById('id_lenght'))
You're attempting to call the undefined .getDate() on a string object, so you'll get an error.
Here's what you need to do. That string (assuming it's in ISO format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS) can be used to create a new Date.
start = new Date(document.getElementById('id_start').value);
Now start is actually a date object, and you can add the length in days to get your end date.
Of course, you'll want to make sure the input is in an acceptable format! There's a lot of 'date picker' options (jQueryUi, for example, has a datepicker), and if you're only interested in supporting Chrome, the works well.

EmberJS: Bind input value to Controller property and run filter on change

I'm trying to bind a input's value to it's controller's property to keep it updated when the property changes. When the input value has changed, I then need to fire a filter method.
I found some examples, but I'm either doing something wrong or they no longer apply as they are a few months old and Ember has gone through some changes.
JSBin: http://emberjs.jsbin.com/oTiKiDI/1/edit
If you were able to manipulate the this.inputDate property directly, as you're doing in your jsbin, I believe it wouldn't properly fire any observers when you change the value. So rather than manipulating this.inputDate.anything, you should this.set('inputDate', [whatever]).
A Date().getDate() returns just the day of the month, not any usable proxy to a Date object, and adding 1 to that value will not add one day to your Date(). I prefer to use http://momentjs.com/ to manipulate dates painlessly and semantically.
http://emberjs.jsbin.com/oKehAYE/3/edit works, and has filterDate pulled out of the actions hash and turned into an observer. If you uncomment the alert() line it will do something when the inputDate changes. It probably doesn't work ideally, since when the page initially renders you have a Date object, and then once you change it you have a Moment—you might as well initialize inputDate with moment(new Date)—but I leave that as an exercise for the reader.

FullCalendar Select Method

I am creating a interactive calendar using FullCalendar but I have run into a nice to have snag.
When the person makes the hour range selection (click and drag) I have a dialog open and allows the user to title their event and modify the date/time selection if needed. What I would like is to re-render the selection with the new date/time selection from the dialog if it changes.
Currently when I run the select method my selection area is just removed from the view, I want it to stay and be updated to the current selection.
Here is my code
$('#UserCalendarToHour, #UserCalendarToMin').change(function(){
var allDay = false;
var startDate = new Date($('#UserCalendarFromDate').val()+' '+$('#UserCalendarFromHour').val()+':'+$('#UserCalendarFromMin').val());
var endDate = new Date($('#UserCalendarToDate').val()+' '+$('#UserCalendarToHour').val()+':'+$('#UserCalendarToMin').val());
if($('#UserCalendarAllDay').is(':checked')){
allDay = true;
}
$('#calendar').fullCalendar('unselect');
$('#calendar').fullCalendar('select',startDate.toString(),endDate.toString(),allDay);
});
Now what am I missing.
The select method is expecting startDate and endDate as Date objects. You're converting them to text. Also, according to docs,
allDay is a boolean indicating if entire days were selected (days in
month view or the "all-day" slot in the agenda view) or time slots
were selected.
So if you are selecting time slots you need to set it to false.
You'll see it more clearly in the select callback documentation. The same type for arguments seem to apply for the method. It took me a while to realize it. You may get confused with, for instance, the Event Object attributes, which have similar names but are of different type. See it here:
http://arshaw.com/fullcalendar/docs/selection/select_callback/

Categories