We had to create a web part to show contents from Calendar list in more intuitive way which looks pleasing to eyes and has same design language as whole site. OOTB web part were not enough sufficient, so we decided to go with Angular & REST services of SharePoint 2013.
Here is the final output:
Note: I saw this free calendar template online and use it as boiler plate to get my result but now I can’t find the link to it. If you find it please let me know, I would love to give credit to the original creator.
Features:
- Big current month at top
- Title, Date & Time, location & description in one view.
- Have navigation arrows to see previous & next month events
- By default shows current month events and hide the past events of the month
- Use of modern icons for time & location for visual impact
Steps:
Create a Calendar list in your site and name it CompanyCalendar.
Add powerful moment.js reference to your page layout or masterpage or just for this web part, which ever the scope you are building the solution and going to use it.
Next is to create a javascript file that will do all the logic which will look like:
/***************************************************************** Company Calendar ******************************************************************/ var angCXCompanyCalendarApp = angular.module('CXCompanyCalendarApp', []); angCXCompanyCalendarApp.service('CXCompanyCalendarService', ['$http', '$q', function($http, $q) { //Get the courses this.getItems = function(eventdate) { var year = 0; var month = 0; if (!eventdate) { eventdate = moment(); } var date = new Date(); var dfd = $q.defer(); var fullUrl = _spPageContextInfo.webAbsoluteUrl + "/_vti_bin/listdata.svc/CompanyCalendar?"; fullUrl += "$filter=(month(StartTime) eq " + moment(eventdate).format('M') + " and EndTime ge datetime'" + date.toISOString() + "')&$orderby=StartTime"; $.ajax({ url: fullUrl, type: "GET", headers: { "accept": "application/json;odata=verbose" }, success: function(data) { dfd.resolve(data.d.results); }, error: function(xhr) { dfd.reject('Error : (' + xhr.status + ') ' + xhr.statusText + ' Message: ' + xhr.responseJSON.error.message.value); } }); return dfd.promise; } }]); angCXCompanyCalendarApp.controller('CXCompanyCalendarCtrl', function($scope, CXCompanyCalendarService) { CXCompanyCalendarService.getItems('').then( function(result) { var cnow = moment(); setitems(cnow, result); }, function(reason) { //$scope.errMessage = reason; }); $scope.getitem = function(value, calendarDate) { var cnow = moment(calendarDate); if (value == '0') { cnow = cnow.subtract(1, 'month'); } else { cnow = cnow.add(1, 'month'); } CXCompanyCalendarService.getItems(cnow).then( function(result) { setitems(cnow, result); }, function(reason) { //$scope.errMessage = reason; }); } function setitems(cnow, result) { $scope.calender = []; $scope.calender.date = cnow; $scope.calender.name = moment(cnow).format("MMMM YYYY"); $scope.calender.events = []; angular.forEach(result, function(event) { var eTime = ''; var sDate = moment(event.StartTime).add(5, 'h'); var eDate = moment(event.EndTime).add(5, 'h'); if (event.AllDayEvent) { eTime = 'All day event'; } else { eTime = moment(sDate).format("HH:mm") + ' - ' + moment(eDate).format("HH:mm"); } var newEvent = { eventday: moment(event.StartTime).format("DD"), endday: moment(event.EndTime).format("DD"), month: moment(event.StartTime).format("MMM"), title: event.Title, location: event.Location, eventtime: eTime, description: event.Description } $scope.calender.events.push(newEvent); }); } });
In this code, simple logic is applied. I’m getting today date & month and based on that getting events of this month starting from current date. On click on next or previous buttons, adding or subtracting 1 from from current month and hitting the server to get new values.
Now the code along with html we need to add in script editor web part to render this output, will look like this:
<script> var calendarListTitle = 'Social Calendar'; </script> <div ng-controller="CXCompanyCalendarCtrl" class="cx-events"> <div id="cx-events"> <div class="cx-nav-container"> <h2>{{calender.name}}</h2> <i class="fa fa-chevron-circle-left cx-arrow-left" ng-click="getitem('0',calender.date)"></i><i class="fa fa-chevron-circle-right cx-arrow-right" ng-click="getitem('1',calender.date)"></i> </div> <div class="cx-events-container"> <ul> <li class="cx-event" ng-repeat="event in calender.events"> <div class="cx-date" ng-switch on="event"> <div class="cx-day">{{event.eventday}}</div> <div class="cx-day_end" ng-class="{'cx-hide' : event.eventday === event.endday}"> - {{event.endday}}</div> <div class="cx-month">{{event.month}}</div> </div> <div class="cx-content"> <ul> <li class="cx-name"> <h4>{{event.title}}</h4> </li> <li class="cx-time"><i class="fa fa-clock-o"></i> {{event.eventtime}} <i class="fa fa-location-arrow"></i> {{event.location}}</li> <li class="cx-description"> {{event.description}} </li> </ul> </div> </li> <li class="cx-event" ng-if="calender.events.length < 1"> <div class="cx-noevents"> No events available. </div> </li> </ul> </div> </div> </div>
Last step is to add CSS, which looks like.
/* #cx-events default styles ================================================== */ .cx-hide { display: none; } #cx-events h1, #cx-events h2, #cx-events h3, #cx-events h4, #cx-events h5, #cx-events h6 { color: #666666; } #cx-events h2 { display: inline; font-weight: lighter; margin: 0; padding: 0; padding-right: 10px; } .cx-nav-arrows { display: inline; } .cx-nav-container i { display: inline; font-size: 24px; padding: 2px; color: #666666; line-height: inherit; } .cx-nav-container i:hover { cursor: pointer; } .cx-event { border: 2px solid #ebebeb; border-bottom: 0px; overflow: hidden; } .cx-events-container &gt; ul &gt; li:last-child { border-bottom: 2px solid #ebebeb; } .cx-events-container ul { list-style-type: none; margin: 0; padding: 0; } .cx-date { float: left; width: 70px; height: 90px; color: #FFF; background: #666666; padding-left: 5px; padding-right: 5px; } .cx-content { overflow: hidden; height: 90px; } .cx-day { font-size: 20px; float: left; width: 30px; padding-top: 10px; } .cx-day_end { font-size: 14px; padding-top: 10px; } .cx-month { font-size: 24px; left: 5px; bottom: 5px; width: 30px; text-transform: uppercase; } .cx-name h4 { font-weight: 600; } .cx-content { padding-top: 5px; padding-left: 5px; } .cx-noevents { font-size: 14px; height: 80px; padding: 15px; } .cx-event:hover { background: #f2f3f1; /* Old browsers */ background: -moz-linear-gradient(top, #f2f3f1 0%, #f4f4f4 58%, #fafafa 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f2f3f1), color-stop(58%,#f4f4f4), color-stop(100%,#fafafa)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #f2f3f1 0%,#f4f4f4 58%,#fafafa 100%); /* Chrome10+,Safari5.1+ */ background: -o-linear-gradient(top, #f2f3f1 0%,#f4f4f4 58%,#fafafa 100%); /* Opera 11.10+ */ background: -ms-linear-gradient(top, #f2f3f1 0%,#f4f4f4 58%,#fafafa 100%); /* IE10+ */ background: linear-gradient(to bottom, #f2f3f1 0%,#f4f4f4 58%,#fafafa 100%); /* W3C */ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f2f3f1', endColorstr='#fafafa',GradientType=0 ); /* IE6-9 */ } /* ================================================== */
And that’s it, you will now have responsive calendar agenda widget.
Known limitation: Currently this code won’t handle repeating events properly, will only show it first occurrence. It is due to how SharePoint saves the reoccurring events.
More Widgets like this to follow soon.
this code is not working. please provide a working code.
Hi Divesh, Sorry this is very old code and I don’t have it any more, and even Angular also has been updated. You can use the idea and look for pnpjs to make it work.
Could you please attach the files? I don’t have many experience with .js and SharePoint. I really like how it looks.
It is an old project, have to look for the files. But all the code is in blog so just copy and paste it to create your files and add it to the Sharepoint
Hi,
I tried implementing this calendar widget, but can’t get it to work. Do you have a link to download the files? The Javascript part seems to have a lot of extra code that doesn’t seem to belong there for example the "
Thanks,
AJ
Sorry for replying late, I have fixed the javascript. WordPress somehow encoded the & and “.
Let me know if still have issues.
I attempted to add this code today but unfortunately I am not able to get it to work. I debugged and it looks like it needed Angular so I added that but it still does not work..it just says
{{calender.name}}
{{event.title}}
{{event.location}}
etc
Hit F12 for dev toolbar and see if any error. It most likely the App is not mounted or missing a reference.
First try with simple example to get the Angularjs working and also it version 1 not the latest Angularjs