Last active
December 18, 2015 04:48
-
-
Save RhysC/5727547 to your computer and use it in GitHub Desktop.
Knockout bindable date selector view model using moment to assist with date parameter and ensuring valid dates are shown. This code only shows valid dates in the date range supplied. Dependencies : knockout, moment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <reference path="~/Scripts/ThirdParty/knockout-2.1.0.debug.js" /> | |
/// <reference path="~/Scripts/ThirdParty/moment.js" /> | |
function YearSelector(earliestDate, latestDate) { | |
var self = this; | |
self.earliestDate = earliestDate; | |
self.latestDate = latestDate; | |
var getYears = function () { | |
var years = []; | |
for (var i = latestDate.year() ; i >= earliestDate.year() ; i--) { | |
years.push(i); | |
} | |
return years; | |
}; | |
self.Years = getYears(); | |
self.Year = ko.observable(self.Years[0]); | |
self.SetDate = function (year) { | |
if (year < self.Years[self.Years.length - 1]) { | |
self.Year(self.Years[self.Years.length - 1]); | |
} else { | |
if (year > self.Years[0]) { | |
self.Year(self.Years[0]); | |
} else { | |
self.Year(year); | |
} | |
} | |
}; | |
return self; | |
} | |
function MonthYearSelector(earliestDate, latestDate) { | |
YearSelector.call(this, earliestDate, latestDate); | |
var self = this; | |
//NOTE these are .Net values, not javascript values, (Jan in js is 0, whereas in .Net it is 1) | |
self.RawMonths = [{ Display: "January", Value: 1 }, | |
{ Display: "February", Value: 2 }, | |
{ Display: "March", Value: 3 }, | |
{ Display: "April", Value: 4 }, | |
{ Display: "May", Value: 5 }, | |
{ Display: "June", Value: 6 }, | |
{ Display: "July", Value: 7 }, | |
{ Display: "August", Value: 8 }, | |
{ Display: "September", Value: 9 }, | |
{ Display: "October", Value: 10 }, | |
{ Display: "November", Value: 11 }, | |
{ Display: "December", Value: 12 }]; | |
self.Month = ko.observable(); | |
self.Months = ko.computed(function () { | |
if (self.Year() == self.earliestDate.year()) { | |
if (self.Month() < self.earliestDate.month() + 1) { | |
self.Month(self.earliestDate.month() + 1); | |
} | |
return self.RawMonths.slice(self.earliestDate.month()); | |
} | |
if (self.Year() == self.latestDate.year()) { | |
if (self.Month() > self.latestDate.month() + 1) { | |
self.Month(self.latestDate.month() + 1); | |
} | |
return self.RawMonths.slice(0, self.latestDate.month()); | |
} | |
return self.RawMonths; | |
}); | |
self.Month(self.Months()[0].Value); | |
return self; | |
} | |
function DateSelector(earliestDate, latestDate) { | |
MonthYearSelector.call(this, earliestDate, latestDate); | |
var self = this; | |
self.RawDays = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]; | |
self.Day = ko.observable(); | |
self.Days = ko.computed(function () { | |
if (self.Year() == self.latestDate.year() && self.Month() == (self.latestDate.month() + 1)) { | |
if (self.Day() > self.latestDate.date()) { | |
self.Day(self.latestDate.date()); | |
} | |
return self.RawDays.slice(0, self.latestDate.date()); | |
} | |
if (self.Year() == self.earliestDate.year() && self.Month() == (self.earliestDate.month() + 1)) { | |
if (self.Day() < self.earliestDate.date()) { | |
self.Day(self.earliestDate.date()); | |
} | |
return self.RawDays.slice(self.earliestDate.date() - 1); | |
} | |
var daysInMonth = moment([self.Year(), self.Month() - 1, 1]).daysInMonth(); | |
if (self.Day() > daysInMonth) { | |
self.Day(daysInMonth); | |
} | |
return self.RawDays.slice(0, daysInMonth); | |
}); | |
self.Day(self.Days()[0]); | |
self.DateText = ko.computed({ | |
read: function () { | |
return moment([self.Year(), self.Month() - 1, self.Day()]).format('DD/MM/YYYY');//http://momentjs.com/docs/#/displaying/format/ | |
}, | |
//write allows us | |
write: function (value) { | |
var attemptedDate = moment(value, 'DD/MM/YYYY'); | |
if (attemptedDate.isValid() && attemptedDate.isAfter(earliestDate) && attemptedDate.isBefore(latestDate)) { | |
self.Year(attemptedDate.years()); | |
self.Month(attemptedDate.months()+1); | |
self.Day(attemptedDate.date()); | |
}//else do nuffin! | |
}, | |
owner: this | |
}); | |
return self; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <reference path="~/Scripts/ThirdParty/jasmine/jasmine.js"/> | |
/// <reference path="~/Scripts/ThirdParty/knockout-2.1.0.debug.js" /> | |
/// <reference path="~/Scripts/ThirdParty/moment.js" /> | |
/// <reference path="~/Scripts/ViewModels/DateSelector.js" /> | |
describe("DateSelector", function () { | |
describe("Default", function () { | |
// - 11/Mar/2010 - 5/Aug/2013 | |
var ds = new DateSelector(moment([2010, 3, 11]), moment([2013, 7, 5])); | |
it("should have years in between and including earliest and latest year in Years list", function () { | |
for (var i = 2010; i <= 2013; i++) { | |
expect(ds.Years).toContain(i); | |
} | |
}); | |
it("should have latest year first on the list", function () { | |
expect(ds.Years[0]).toBe(2013); | |
}); | |
it("should have latest year by default", function () { | |
expect(ds.Year()).toBe(2013); | |
}); | |
it("should have first month and day by default", function () { | |
expect(ds.Month()).toBe(1); | |
expect(ds.Day()).toBe(1); | |
}); | |
}); | |
describe("when changing to earliest year", function () { | |
var ds, earliestDate = moment([2010, 2, 11]);//11th of mar 2010 (js month index in 0 based) | |
beforeEach(function () { | |
ds = new DateSelector(earliestDate, moment([2013, 4, 5])); | |
}); | |
describe("given select month is before earliest month", function () { | |
it("should set the month to earliest month", function () { | |
ds.Year(2012); | |
ds.Month(2);//FEB - ie one month earlier than the earliest month in the earliest year | |
ds.Year(earliestDate.years()); | |
expect(ds.Month()).toBe(3); //.Net month index | |
}); | |
}); | |
describe("given selected day is before earliest day", function () { | |
it("should set the Day to earliest day", function () { | |
ds.Year(2012); | |
ds.Month(3); //earliest month | |
ds.Day(1);//before the earliest day for the earliest month | |
ds.Year(earliestDate.years()); | |
expect(ds.Day()).toBe(earliestDate.date()); | |
}); | |
}); | |
}); | |
describe("when changing to latest year", function () { | |
var ds, latestdate = moment([2013, 4, 5]);//5th of Apr 2013 (js month index in 0 based) | |
beforeEach(function () { | |
ds = new DateSelector(moment([2010, 2, 11]), latestdate); | |
}); | |
describe("given select month is after latest month", function () { | |
it("should set the month to latest month", function () { | |
ds.Year(2012); | |
ds.Month(6);//JUN - ie one month later than the latest month in the latest year | |
ds.Year(latestdate.years()); | |
expect(ds.Month()).toBe(5); //.Net month index | |
}); | |
}); | |
describe("given selected day is after latest day", function () { | |
it("should set the Day to latest day", function () { | |
ds.Year(2012); | |
ds.Month(5); //latest month | |
ds.Day(6);//after the latest day for the latest month | |
ds.Year(latestdate.years()); | |
expect(ds.Day()).toBe(latestdate.date()); | |
}); | |
}); | |
}); | |
describe("Days in months", function () { | |
it("should only show valid days in each months", function () { | |
var ds = new DateSelector(moment([2000, 1, 1]), moment([2020, 1, 1])); | |
ds.Year(2011);//non leap year | |
ds.Month(1); | |
expect(ds.Days().length).toBe(31); | |
ds.Month(2); | |
expect(ds.Days().length).toBe(28); | |
ds.Month(3); | |
expect(ds.Days().length).toBe(31); | |
ds.Month(4); | |
expect(ds.Days().length).toBe(30); | |
ds.Month(5); | |
expect(ds.Days().length).toBe(31); | |
ds.Month(6); | |
expect(ds.Days().length).toBe(30); | |
ds.Month(7); | |
expect(ds.Days().length).toBe(31); | |
ds.Month(8); | |
expect(ds.Days().length).toBe(31); | |
ds.Month(9); | |
expect(ds.Days().length).toBe(30); | |
ds.Month(10); | |
expect(ds.Days().length).toBe(31); | |
ds.Month(11); | |
expect(ds.Days().length).toBe(30); | |
ds.Month(12); | |
expect(ds.Days().length).toBe(31); | |
//leap year | |
ds.Year(2012); | |
ds.Month(2); | |
expect(ds.Days().length).toBe(29); | |
}); | |
}); | |
describe("when changing month", function () { | |
it("when current day is greater than last day in selected month, set day to last", function () { | |
var ds = new DateSelector(moment([2000, 1, 1]), moment([2020, 1, 1])); | |
ds.Year(2011);//non leap | |
ds.Month(3); | |
ds.Day(30); | |
ds.Month(2); | |
expect(ds.Day()).toBe(28); | |
}); | |
}); | |
describe("DateText binding", function () { | |
describe("DateText is read correctly", function () { | |
it("gives 02/06/2012", function () { | |
var ds = new DateSelector(moment([2000, 1, 1]), moment([2020, 1, 1])); | |
ds.Year(2012); | |
ds.Month(6); | |
ds.Day(2); | |
expect(ds.DateText()).toBe('02/06/2012'); | |
}); | |
}); | |
describe("DateText is written correctly", function () { | |
it("given 02/06/2012 it get correct date", function () { | |
var ds = new DateSelector(moment([2000, 1, 1]), moment([2020, 1, 1])); | |
ds.DateText('02/06/2012'); | |
expect(ds.Year()).toBe(2012); | |
expect(ds.Month()).toBe(6); | |
expect(ds.Day()).toBe(2); | |
}); | |
it("given 12/06/2012 it get correct date", function () { | |
var ds = new DateSelector(moment([2000, 1, 1]), moment([2020, 1, 1])); | |
ds.DateText('12/06/2012'); | |
expect(ds.Year()).toBe(2012); | |
expect(ds.Month()).toBe(6); | |
expect(ds.Day()).toBe(12); | |
}); | |
}); | |
describe("Invalid datetexts are not assigned", function () { | |
it("given invalid string it doesnt change the existing date", function () { | |
var ds = new DateSelector(moment([2000, 1, 1]), moment([2020, 1, 1])); | |
ds.Year(2012); | |
ds.Month(6); | |
ds.Day(2); | |
ds.DateText('33/33/2012'); | |
expect(ds.Year()).toBe(2012); | |
expect(ds.Month()).toBe(6); | |
expect(ds.Day()).toBe(2); | |
}); | |
it("given date before earliest date it doesnt change the existing date", function () { | |
var ds = new DateSelector(moment([2000, 1, 1]), moment([2020, 1, 1])); | |
ds.Year(2012); | |
ds.Month(6); | |
ds.Day(2); | |
ds.DateText('01/01/1999'); | |
expect(ds.Year()).toBe(2012); | |
expect(ds.Month()).toBe(6); | |
expect(ds.Day()).toBe(2); | |
}); | |
it("given date after latest date it doesnt change the existing date", function () { | |
var ds = new DateSelector(moment([2000, 1, 1]), moment([2020, 1, 1])); | |
ds.Year(2012); | |
ds.Month(6); | |
ds.Day(2); | |
ds.DateText('01/01/2021'); | |
expect(ds.Year()).toBe(2012); | |
expect(ds.Month()).toBe(6); | |
expect(ds.Day()).toBe(2); | |
}); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
currently an error where the latest month is not showing when latest year is selceted. issue with line 61