Dropdown Date Picker Combo Box with Restricted Date Utilizing Lightning Design System and AngularJS

Hello there,

This is one of my favourite Dropdown Date picker as it sanctions to put a restriction on a future or past date predicated on categorical conditions, Something Fascinating? Excited? We are! Read on..200 (3)ggfg.gif

A couple of days back, I got a prerequisite where system users were requesting the benefit to limits either future date or past date in light of a specific condition culled on UI. So to achieve the same, I mentally conceived of going with dropdown date picker instead of calendar view Date picker.

On the off chance that you have straight forward requisite of culling date value from UI, you could preferably have an optical canvassing of my another blog- Built Datepicker Using Salesforce Lightning Design System.

Initial setup : 

Afore starting with Initial setup, get acquainted with Lightning introduction

  1. Download the ‘Salesforce Lightning Design System’ Custom Scoped CSS from here , place your desired scoping class name into the “Scoping class” input and click Generate CSS. This Scoping class wraps your content using the Lightning Design System to avoid CSS conflicts. Scoping class name example : slds
  2. Download the scoped version of the Design Systems files and upload Zip folder as your static resource.
  3. As I am using Angular Js in my code, I have included angular Js file in code. Download AngularJS file from here and upload to a static resource.

Lets have a look at the restriction scenarios that I have achieved using Lightning Design System and AngularJS. Here, based on Event status I am deciding whether to block future dates or past dates.

  • Restricting Future date, with conditional checking.
slds-datepicker-new-1a

Future Dates are not available to select

In above GIF image, we can see that status is completed which indirectly indicates that event is completed and we can’t select any future date from dropdown, so the date picker will restrict all the future dates. Above GIF image was created on 17th Nov, so it is not showing any value in dropdown which allows the user to select future dates.

  •  Opening  restriction to Future date and restricts Past date, with conditional checking
SLDS Datepicker New 1b.gif

Future Dates are available to select

By looking at above GIF image it is pretty much clear that if the status is completed it won’t allow the user to select future date i.e date after 17th Nov 2016, but what if user change status to New ??? So as soon as user change status value to New, dynamic dropdown values are updated and Damn! Users are allowed to select the future Date🙂. But in this scenario, users aren’t allowed to select the past date i.e. date before 1st Jan 2016.

Note : Based on users criteria developer can make changes to AngularJs/Javascript to alter the condition as well as the restrictive date.

It’s time to check the code !! Inline comments are added to specific details about code components.

<apex:page showHeader="false" sidebar="false" docType="html-5.0" standardStylesheets="false" applyBodyTag="false" applyHtmlTag="false">
<html ng-app="SLDSDatepickerExample2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<!-- Include Lightning design System CSS -->
							<link href="{!URLFOR($Resource.LightningDesignSystem,'assets/styles/salesforce-lightning-design-system-vf.min.css')}" rel="stylesheet" />

<!-- Additional Style for page container -->
<style>
body{
margin:3px !important;
}
.pagecontainer{
padding:50px;
}
</style>

<script type="text/javascript">

 // define the Angular app
 var groupEventApp = angular.module('SLDSDatepickerExample2', []);
 groupEventApp.controller('HomeController',['$log','$scope',function($log,$scope){

 $scope.statusVal = 'Completed'; //Initialize status value
 $scope.statusPicklistValue = ['New','Completed']; //Initialize status Picklist Value

 // For date picker
 var dt = new Date();

 var yearFilter = parseInt(dt.getFullYear(),10)-1; // Get the Previous Year

 // List Months
 var MonthsT = [{month:'Jan',code:'01'},{month:'Feb',code:'02'},{month:'Mar',code:'03'},{month:'Apr',code:'04'},
 {month:'May',code:'05'},{month:'Jun',code:'06'},{month:'Jul',code:'07'},{month:'Aug',code:'08'},
 {month:'Sep',code:'09'},{month:'Oct',code:'10'},{month:'Nov',code:'11'},{month:'Dec',code:'12'}];
 var monthsWith31 = [1,3,5,7,8,10,12]; // List of the month having 31 days
 var monthsWith30 = [4,6,9,11]; // // List of the month having 30 days

 //Attention --> In Above lists feb (code - 2) is not added as it has 28 or 29 days based on Leap Year. 

 // Function to check LeapYear - Its also important to consider Leap Year.
 checkLeapYear = function(yr){
 return (yr%4 == 0 ? 29 : 28);
 }

 // Function will return number of days based on selected month (considering Leap Year as well)
 returnMonthLength = function(yr,mn){
 if(monthsWith31.indexOf(mn) != -1){
 return 31;
 }else if(monthsWith30.indexOf(mn) != -1){
 return 30;
 }else{
 return checkLeapYear(yr);
 }
 }

 $scope.MonthsT = []; 

 //Initialize list of Month (Considering an event is already completed, so limiting future month(s) )
 for(var m = 0; m < parseInt(dt.getMonth(),10)+1 ; m++){
 $scope.MonthsT.push(MonthsT[m]);
 }

 $scope.daysList = [];
 //Initialize list of days (Considering an event is already completed, so limiting future day(s) )
 angular.forEach($scope.MonthsT, function(rec, key){
 if((parseInt(dt.getMonth(),10)+1) == parseInt(rec.code,10)){

 $scope.currentMonth = $scope.MonthsT[key];
 for(var t = 0;t < parseInt(dt.getDate(),10); t++){
 if(t < 9){
 $scope.daysList.push("0"+(t+1)+"");
 }else{
 $scope.daysList.push(""+(t+1)+"");
 }

 }
 }

 });

 // Initialize today's Date
 $scope.currentDate = ""+(parseInt(dt.getDate(),10) < 10 ? '0'+dt.getDate() : dt.getDate())+"";
 $scope.years = [];
 // Initialize previous and current Year
 for(var i = yearFilter;i < yearFilter+2;i++){
 $scope.years.push(""+i+"");
 }

 $scope.currentYear = ""+dt.getFullYear()+"";

 // Below function will be called when month is being changed on UI, so based on month and status we have to push the new value
 $scope.addDaysList = function(){
 $scope.daysList = [];

 // Get the selected month lengh (Number of days in Month)
 var monthLength = returnMonthLength(parseInt($scope.currentYear,10),parseInt($scope.currentMonth.code,10)); 

 // If status completed than we have restricts the future dates
 if($scope.statusVal == 'Completed' && parseInt($scope.currentMonth.code,10) == (parseInt(dt.getMonth(),10)+1) && parseInt($scope.currentYear,10) == parseInt(dt.getFullYear(),10)){
 for(var t = 0;t < parseInt(dt.getDate(),10); t++){
 if(t < 9){
 $scope.daysList.push("0"+(t+1)+"");
 }else{
 $scope.daysList.push(""+(t+1)+"");
 }
 }
 }
 // If status is New than we have to allow the Future Date as well.
 else{
 for(var t = 0;t < monthLength; t++){
 if(t < 9){
 $scope.daysList.push("0"+(t+1)+"");
 }else{
 $scope.daysList.push(""+(t+1)+"");
 }
 }
 }

 $scope.currentDate = "0"+1+"";
 // update display date field
 $scope.activityDate = $scope.currentMonth.month+'-'+$scope.currentDate+'-'+$scope.currentYear;

 }

 // Update Dates fields - if status is changed to Complete (Restricts Future dates as compared to current date)
 setDatesForCompleted = function(){
 var yearFilter = parseInt(dt.getFullYear(),10)-1; 

 $scope.MonthsT = []; 

 for(var m = 0; m < parseInt(dt.getMonth(),10)+1 ; m++){
 $scope.MonthsT.push(MonthsT[m]);
 }

 $scope.daysList = [];

 angular.forEach($scope.MonthsT, function(rec, key){
 if((parseInt(dt.getMonth(),10)+1) == parseInt(rec.code,10)){

 $scope.currentMonth = $scope.MonthsT[key];
 for(var t = 0;t < parseInt(dt.getDate(),10); t++){
 if(t < 9){
 $scope.daysList.push("0"+(t+1)+"");
 }else{
 $scope.daysList.push(""+(t+1)+"");
 }

 }
 }
 });

 $scope.currentDate = ""+(parseInt(dt.getDate(),10) < 10 ? '0'+dt.getDate() : dt.getDate())+"";
 //yearFilter = 2015;
 $scope.years = [];
 //$log.info('YEAR N',yearFilter);
 for(var i = yearFilter;i < yearFilter+2;i++){
 $scope.years.push(""+i+"");
 }

 $scope.currentYear = ""+dt.getFullYear()+"";
 }
 // Update Dates fields - if status is changed to New (Open restriction for Future dates)
 setDatesForNonCompleted = function(){
 var yearFilter = parseInt(dt.getFullYear(),10); 

 $scope.MonthsT = []; 

 for(var i = 0;i < MonthsT.length;i++){
 $scope.MonthsT.push(MonthsT[i]);
 }

 $scope.daysList = [];

 angular.forEach($scope.MonthsT, function(rec, key){
 if((parseInt(dt.getMonth(),10)+1) == parseInt(rec.code,10)){
 $scope.currentMonth = $scope.MonthsT[key];

 var monthLength = returnMonthLength(parseInt(dt.getFullYear(),10),parseInt(rec.code,10));
 for(var t = 0;t < monthLength; t++){
 if(t < 9){
 $scope.daysList.push("0"+(t+1)+"");
 }else{
 $scope.daysList.push(""+(t+1)+"");
 }
 }
 }
 });

 $scope.currentDate = ""+(parseInt(dt.getDate(),10) < 10 ? '0'+dt.getDate() : dt.getDate())+"";
 $scope.years = [];
 for(var i = yearFilter;i < yearFilter+2;i++){  $scope.years.push(""+i+"");  }    $scope.currentYear = ""+dt.getFullYear()+"";  }    // Update Date fields value based on status value  $scope.onStatusChange = function(){  if($scope.statusVal != 'Completed'){  setDatesForNonCompleted();    }else{  setDatesForCompleted();  }  // update display date field   $scope.activityDate = $scope.currentMonth.month+'-'+$scope.currentDate+'-'+$scope.currentYear;  }    // Update Date fields values when Year is changed (Considering leap year and status as well)  $scope.onYearChange = function(){  if(parseInt(dt.getFullYear(),10) == parseInt($scope.currentYear,10)){  // current year is equal to selected year   if($scope.statusVal == 'Completed' || $scope.statusVal == undefined){  setDatesForCompleted();  }else{  setDatesForNonCompleted();  }  }else if(parseInt(dt.getFullYear(),10) > parseInt($scope.currentYear,10)){
 // current year is greater than selected year
 $scope.MonthsT = []; 

 for(var i = 0;i < MonthsT.length;i++){
 $scope.MonthsT.push(MonthsT[i]);
 }
 $scope.currentMonth = $scope.MonthsT[11];

 $scope.daysList = [];
 for(var t = 0;t < 31; t++){
 if(t < 9){
 $scope.daysList.push("0"+(t+1)+"");
 }else{
 $scope.daysList.push(""+(t+1)+"");
 }
 }

 $scope.currentDate = "0"+1+"";

 }else if(parseInt(dt.getFullYear(),10) < parseInt($scope.currentYear,10)){
 // current year is less than selected year
 var selectedYear = $scope.currentYear;
 setDatesForNonCompleted();
 $scope.currentYear = selectedYear;
 }

 // update display date field
 $scope.activityDate = $scope.currentMonth.month+'-'+$scope.currentDate+'-'+$scope.currentYear;
 }

 // TO display on the UI - Initial Page Loading
 $scope.activityDate = $scope.currentMonth.month+'-'+$scope.currentDate+'-'+$scope.currentYear;

 // update activityDate variable when date is changed.
 $scope.updateActivityDate = function(){
 $scope.activityDate = $scope.currentMonth.month+'-'+$scope.currentDate+'-'+$scope.currentYear;
 } 

 }]);

 </script>
</head>
<body ng-controller="HomeController">
<div class="slds">
<!-- PAGE HEADER -->
<div class="slds-page-header" role="banner">
<div class="slds-media slds-media--center">
<div class="slds-media__figure">
<svg aria-hidden="true" class="slds-icon slds-icon-standard-opportunity">
<use xlink:href="{!URLFOR($Resource.LightningDesignSystem,'assets/icons/action-sprite/svg/symbols.svg#new_event')}"></use>
</svg></div>
<div class="slds-media__body">
<h1 class="slds-page-header__title slds-truncate" title="Group Events">Date Picker Combo Box with Dropdown</h1>
</div>
</div>
</div>
<!-- / PAGE HEADER -->
<div class="pagecontainer">
<div class="slds-form--stacked">
<!-- Status Field -->
<div class="slds-form-element">
<label class="slds-form-element__label" for="select01">
<abbr class="slds-required" title="required">*</abbr>
Status
</label>
<div class="slds-form-element__control">
<div class="slds-select_container">
<select id="select01" name="select01" ng-required="true" class="slds-select" ng-model="statusVal" ng-options="o as o for o in statusPicklistValue" ng-change="onStatusChange()"></select></div>
</div>
</div>
<!-- / Status Field -->
<!-- Date Field -->
<div class="slds-form-element" >
<label class="slds-form-element__label" >Campaign Event Date</label>
<div >
<div class="slds-form-element" style="width: 14%;float: left;margin-right: 3px;margin-top: 0px;">
<label class="slds-form-element__label" for="select402">Year</label>
<div class="slds-form-element__control" >
<select id="select402" name="select402" class="slds-select" ng-model="currentYear" ng-options="o as o for o in years" ng-change="onYearChange()" ></select></div>
</div>
<div class="slds-form-element" style="width: 14%;float: left;margin-right: 3px; margin-top: 0px;">
<label class="slds-form-element__label" for="select40">Month</label>
<div class="slds-form-element__control" >
<select id="select40" name="select40" class="slds-select" ng-model="currentMonth" ng-options="o.month for o in MonthsT" ng-change="addDaysList()"></select></div>
</div>
<div class="slds-form-element" style="width: 14%;float: left;margin-right: 3px;margin-top: 0px;">
<label class="slds-form-element__label" for="select401">Day</label>
<div class="slds-form-element__control" >
<select id="select401" name="select401" class="slds-select" ng-model="currentDate" ng-options="o as o for o in daysList" ng-change="updateActivityDate()"></select></div>
</div>
</div>
</div>
<!-- /Date Field -->

<!-- Display Selected On page -->
<div class="slds-form-element" style="margin-top: 50px;">
<h3 class="slds-section-title--divider">SELECTED DATE : {{activityDate}}</h3>
</div>
<!-- /Display Selected On page --></div>
</div>
</div>
<!-- Include Angular Resources -->
<apex:includeScript value="{!URLFOR($Resource.AngularResources,'AngularBootStapNew/js/angular.min.js')}"/>
</body>
</html>
</apex:page>

Note : You need to replace the all static resource name with your static resource name.

Code is also available on Github 

Note : AngularJs/Javascript code also takes care of the leap year.

If you have a requirement with other specific condition with a set of restriction, you are free to modify your AngularJs code.

So That’s all ! we have just created the Date picker Combo Box with Dropdown using Lightning Design System and AngularJS.

What’s Next ?

200 (20).gif

Keep your eyes on upcoming blog entry – Build Multiselect Picklist Utilizing Salesforce Lightning Design System and AngularJs.

I would appreciate if you could share your thoughts in the comments section.

Happy Coding 🙂

Thank you.

Advertisements

2 thoughts on “Dropdown Date Picker Combo Box with Restricted Date Utilizing Lightning Design System and AngularJS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s