Monday, May 23, 2016

How we use Kendo UI on the Collab365 Platform

2 years ago a simple suggestion by a friend on email turned into an idea that has resulted in us delivering our 7th online conference. We’ve always had a passion to get involved in the community and to attempt to bring top quality training to those that need it. To achieve this we’ve needed to keep costs to a minimum and that’s why we built our own virtual conference platform.

This post is part of our ‘Behind the Scenes’ series and is aimed at developers who are interested in how we used the highly popular Kendo UI by Telerik, a Progress Company, to build a large chunk of our conference User Interface. We’ve used it in lots of places, but this post will concentrate on the following 3:

  • Conference Agenda and Timeline – built with Kendo UI Scheduler
  • Session Room – built using several Kendo UI controls as well as an awesome usage of MVVM
  • ‘Which room are people in’ control – built using Kendo UI Chart

A word about our data...

Before we get into how we put the Collab365 Agenda control together, I just wanted to let you in on a secret! When requesting data about our conference (such as speakers, track or session information), we never go direct to our SharePoint lists. You may think this is a strange thing to do, but if you consider that we’re limited by platform size and have to scale to 1,000s of users (with a burst at any time), we had to think seriously about how we scaled SharePoint.

We also cache as much as possible by using a CDN. This means that that Javascript & CSS files, as well as images are all cached outside of SharePoint. Saving potentially 10,000s of requests per second at peak!

If you use SharePoint for internal needs and have a version above Foundation you will get the benefits of Object caching, page output caching and can easily scale to your maximum estimated load as you know how many staff you have!

Note! As SharePoint lists all support returning data using REST it's also very simple to bind them to all of the Kendo UI controls.  

Conference Agenda

When running either a physical or virtual conference, one of the key features you need is for attendees to be able to view the agenda before it actually begins (to allow them to build their own agenda), and also during the conference so they can work out which virtual room to go to.

  • Agenda Requirements​
  • We wanted something that could be filtered by parameters such as skill level, track, speaker, audience etc.
  • It needed to render nicely on mobile devices.
  • The ability for attendees to visualise tracks across a time slot.
  • Easy to put on both our conference platform and also WordPress site.
  • Ability to control what’s displayed in the event section.
  • Something that can work easily with our data, which is in JSON format.

How did we build it?

To build the agenda we used the Kendo UI Scheduler control, which is a purpose built control that allows you to display data in a calendar format. It has two views:

The 'Agenda View'​

This view is the traditional view that allows you to see what’s happening hour by hour. (There are a few more views supported by the scheduler, but we hid them as they weren’t needed).

The 'Timeline view'

The Timeline view is most useful for our management team as it easily lets us see where we may have conflicts (such as a similar sessions being broadcast at the same time).

Databinding
One of the other cool things about using the scheduler is that it supports JSON out of the box and the OData protocol, meaning that all we need to do is give it our list of cached ‘Sessions’ in JSON format. You also tell the control about the relevant metadata and then let it take away all of the pain of rendering the grid.

dataSource: {filter: mySessionFilters,data: data,
schema: {
model: {
id: "ID",
status: function() {
return getClbSessionStatus(this);
},
hashtag: function() {
return this.clbSessionHashtag.substring(1);
},
sessionRoomId: function() {
return this.clbSessionHashtag.substring(1).replace('#', '');
},
speakerPhoto: function() {
return tenantUrl + this.Session_x0020_Speaker_x003A_Smal;
},
conferenceName: function() {
return conferenceTitle;
},
tenantUrl: function() {
return tenantUrl;
},
status: function() {
if (!this.sessionStatus) {
setClbSessionStatus(this);
}
return this.sessionStatus;
},
statusHtml: function() {
if (!this.sessionStatusHtml) {
setClbSessionStatusHtml(this);
}
return this.sessionStatusHtml
},
fields: {
taskId: {
from: "ID",
type: "number"
},
title: {
from: "Title",
defaultValue: "No title",
validation: {
required: true
}
},
start: {
type: "date",
from: "clbSessionStartTime",
parse: function(value) {
if (value != null) return new Date(parseInt(value.substr(6)));
else return conferenceStartDate
}
},
end: {
type: "date",
from: "clbSessionEndTime",
parse: function(value) {
if (value != null) return new Date(parseInt(value.substr(6)));
else return conferenceStartDate
}
},
level: {
from: "clbSessionLevel"
},
track: {
from: "clbSessionTrack"
},
topic: {
from: "clbSessionTopic"
},
language: {
from: "clbSessionLanguage"
},
speaker: {
from: "clbSessionSpeaker"
},
audience: {
from: 'clbSessionSuitableFor'
},
speakerId: {
from: 'Session_x0020_Speaker_x003A_ID'
}
}
}
},
},
}

The “data” as passed in via a “data:data” statement happens to be an array JSON object that defines each session.

Filters

The filters you see to select subsets of the agenda were also pretty easy to implement once we’d populated the pull down lists (called DropDownLists within Kendo UI) with all of the possible values. The great thing is that, as all of our JSON is downloaded to the browser as a file, we don’t need to go back to the server to filter.

initFilters = function(data) {var filterValues = getFilterValues(data);container.find(".levelsFilter").kendoMultiSelect({
placeholder: "Select skill levels...",
dataSource: {
data: filterValues.levels
}
});
container.find(".tracksFilter").kendoMultiSelect({
placeholder: "Select tracks...",
dataSource: {
data: filterValues.tracks
}
});
container.find(".speakersFilter").kendoMultiSelect({
placeholder: "Select speakers...",
dataSource: {
data: filterValues.speakers
}
});
container.find(".audiencesFilter").kendoMultiSelect({
placeholder: "Select audiences...",
dataSource: {
data: filterValues.audiences
}
});
container.find(".topicsFilter").kendoMultiSelect({
placeholder: "Select topics...",
dataSource: {
data: filterValues.topics
}
});
container.find(".statusFilter").kendoMultiSelect({
placeholder: "Select status...",
dataSource: {
data: ["Scheduled", "Live", "Finished", "Cancelled"]
}
});
container.find(".agendaFilters select.agendaFilter").change(applyFilters);
}

Responsive

17% of our conference attendees access the site via a mobile or tablet.

That’s nearly 1 in every 5 attendees accessing the pages with a small display. It’s often hard to show lots of data nicely onto a small screen, but with some media queries the scheduler does a good job.

Here’s how it looks on an iPhone 5:

Portable to WordPress

One other major requirement we have is that we need to publish the agenda long before we open the conference platform. This helps us to tell attendees what sessions are available. However, we didn’t want to duplicate data.

Luckily, as Kendo UI is purely a client-side library (with no server requirements), and also because we don’t go direct to SharePoint lists, it’s very easy to include and reuse it in any web platform.

We built the control to be portable and with very little JavaScript and with a few includes plus some initialisation code we were able to get it rendering inside of WordPress with very little effort.

Here’s the code to bring it in:

<link href="http://ift.tt/1WKmhAR" rel="stylesheet" /><link href="http://ift.tt/1TysIn6" rel="stylesheet" /><script type="text/javascript" Src="http://ift.tt/1WKlRdK"></script>
<script src="http://ift.tt/1Tyt8tw Library/Conference/Assets/scripts/agenda.js"></script>
<script src="http://ift.tt/1Tyt8tw Library/Conference/Assets/scripts/agenda-template.js"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
var options = {
popout: false,
height: 700,
currentTime: clbCom.getSvrTime(),
template: clbAgendaTemplate,
dataSource: allSessions.sessions,
conferenceName: clbConferenceSettings().conferenceName,
conferenceStartDateTime: clbConferenceSettings().conferenceStartDateTime,
conferenceEndDateTime: clbConferenceSettings().conferenceEndDateTime,
conferenceSiteUrl: clbConferenceSettings().confSiteUrl,
tenantSiteUrl: clbConferenceSettings().tenantSiteUrl,
landingPageUrl: clbConferenceSettings().landingPageUrl,
enableMyAgenda: false,
showPlanButtons: false
}
jQuery(".agenda").clbAgenda(options);
});
</script>

And here’s how it looks:

The anatomy of a virtual session room

Each session is delivered and presented in its own virtual session room. The session room has the following aspects:​

As with nearly all of the platform, it’s very lightweight when accessing SharePoint. We feed in the same browser cached JSON files for session, speaker and sponsor.

This means most of the UI rendering is done on the client-side rather than the server. To make our UI as clean and maintainable as possible we take advantage of the Kendo UI in-built MVVM framework. This allows us to bind our data (the “model”) to the page (the “view”) without having to write any of that plumbing code that you normally have to do.

Take a look at the code below, you will notice ‘data-bind’ statements which don’t pollute HTML. Those statements are used by the Kendo UI binding framework and they tell Kendo UI what object to extract from the view and replace at the time that it’s rendered.

<SharePoint:SPSecurityTrimmedControl ID="SPSecurityTrimmedControlPlayer1" runat="server" AuthenticationRestrictions="AnonymousUsersOnly"><div class="main-box clearfix"><header class="main-box-header clearfix">
<h2 data-bind="text: Title"></h2>
<div class="cf"></div>
</header>
<div class="main-box-body clearfix">
<div id="playerHeading">
<div id="playerTitle" data-bind="text: clbSubTitle"></div>
<div id="onAir" data-bind="style: {color: clbRoomStatusColor}, text: clbRoomStatus" class="offAirStatus"></div>
</div>
<div class="iframe-container" id="playerContainer">
<div id="noMessage">
<div class="alert alert-block fade in" style="text-align:left;">
<i class="fa fa-info-circle fa-fw fa-lg" style="padding-bottom:10px"></i><strong>Not logged In!</strong>
<p>In order to take advantage of the features and also view the sessions, you need to register for a free account, or login.</p>
<p>
<asp:literal runat="server" Text="&lt;a class='btn btn-primary collreg' href='/sitepages/Summit2016.aspx?Source=" />
<SharePoint:ProjectProperty Property="Url" runat="server" />
<asp:literal runat="server" Text="' &gt; Register &lt;/a&gt;" />
<asp:literal runat="server" Text="&lt;a class='btn btn-primary' href='/_layouts/15/Authenticate.aspx?Source=" />
<SharePoint:ProjectProperty Property="Url" runat="server" />
<asp:literal runat="server" Text="' &gt; Sign In &lt;/a&gt;" />
</p>
</div>
</div>
<div id="playerTools">
<div id="clockLogo">
<img alt="Collab365" class="playerImage" data-bind="attr: { src: clbPlayerLogo }" />
<div id="playerClock">
</div>
</div>
<div id="muteBtn" class='plybtn mute'></div>
<div id="fullScrBrn" class="plybtn fullscreen"></div>
</div>
</div>
</div>
</div>
</SharePoint:SPSecurityTrimmedControl>

The code snippet below illustrates how we create an “observable” object and bind it into “<BODY>” tag in the HTML. The cool thing here is that as the object is ‘observable’ this means that the Kendo UI MVVM framework will keep watching for changes. So if, for example, some user action causes our ObservableRoom to update then the UI also updates automatically. The binding is also two-way meaning that if you had bound “title” to text box, for example, and a user typed in a new title, the backend object would also be updated.

observableRoom = kendo.observable(session);observableRoom.set("clbTwitterHref", "https://twitter.com/search?q=" + session.clbSessionHashtag);observableRoom.set("clbTwitterTitle", "Tweet using " + session.clbSessionHashtag);
observableRoom.set("clbTwitterTxtLink", "Tweets about &quot;#Collab365 Conference&quot;");
observableRoom.set("clbDerivedTitle", session.clbSessionHashtag + " : " + session.clbTimeSlot);
observableRoom.set("clbRoomStatus", "Off Air");
observableRoom.set("docReadHead", "<a href='#'>My Reading Tasks</a>");
observableRoom.set("docReadStrap", "Any recommended or required reading tasks that the Speaker, Anchor or Collab365 Team have assigned to you are listed below.");
observableRoom.set("clbFlagUrl", siteCollectionUrl + "/Style Library/Conference/Assets/img/flags/32/" + session.clbSessionLanguage + ".png");
observableRoom.set("clbPlayerLogo", siteCollectionUrl + "/SiteAssets/sessionlogo.png");
observableRoom.set("clbProfilePictureUrlLarge", "/SiteAssets/img_placeholder.png");
if (observableRoom.clbSessionYouWillLearn == null) {
observableRoom.clbSessionYouWillLearn = '';
}
observableRoom.clbSessionSuitableFor = clbRoom.cleanSPArray(observableRoom.clbSessionSuitableFor);
observableRoom.clbSessionTopic = clbRoom.cleanSPArray(observableRoom.clbSessionTopic);
kendo.bind($('body'), observableRoom);

I am sure you will agree this saves a ton of boilerplate code and reduces the potential for bugs tremendously. If you do any form of complex JavaScript UI always consider using an approach such as this.

What about AngularJS?​

In many cases, you can also easily use AngularJS with Kendo UI. We chose not to as the Kendo UI binding framework was all we needed and didn’t want the extra page weight that Angular brings. However, your case may be different so it’s worth mentioning that you can use it 

Tab Controls​

Our session room contains a lot of information and we needed something to organise it without having an endless vertical scroll. So we opted for Kendo UI TabStrip. It’s responsive out of the box and pretty easy to develop.

Here’s an example of our Chat and Tweet tab

Here’s the mark-up for the Chat and Twitter control:

<div id="quick_post"><div class="main-box clearfix"><header class="main-box-header clearfix">
<h2>Get Social
<span class="chatSponsorContainer">
sponsored by
<a href="http://beezy.net/" target="_blank"><img src="/confs/Summit2016/SiteAssets/beezy-logo-S.png" alt="Beezy" /></a>
</span>
</h2>
</header>
<div class="main-box-body clearfix">
<div class="k-widget k-header k-tabstrip" id="tabstripChat" role="tablist">
<ul class="k-tabstrip-items k-reset">
<li role="tab" class="k-state-active k-item k-tab-on-top k-state-default k-first">
Chat
</li>
<li role="tab">
Tweet
</li>
</ul>
<div class="chatContainer">
<div id="chatApp">
<chat ng-controller="chatCtrl"></chat>
</div>
</div>
<div>
<div class="twitterContainer">
<p class="heading">Join the session conversation on Twitter...
<span class="pull-right twitBtn-container"></span>
</p>
<div class="iframe-container">
<a data-bind="attr: { href: clbTwitterHref, data-widget-id: clbTwitterDataId}" class="twitter-timeline" href="https://twitter.com/search?q=%23SPBiz" data-widget-id="448566281650704385" data-chrome="transparent noheader">
View Tweets about this session.
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

'Which room are people in’ control

The 3rd use of Kendo UI that I wanted to bring to your attention is our really popular chart that sits on the home page. This chart comes into its own during conference hours. Its main purpose is to indicate to attendees how many people are in each session. The UI is built using the Kendo UI Chart control and this what it looks like and how we put it together:

The markup is extremely simple, you just need to declare a “DIV” with an ID that can then be referenced in JavaScript.

The code that brings it all together is as follows.

$("#UsersByRoomChart").kendoChart({theme: "Bootstrap",chartArea: {
height: 250
},
legend: {
visible: false
},
transitions: false,
valueAxis: {
// majorUnit:1,
visible: false,
labels: {
visible: false
},
majorGridLines: {
visible: false
},
line: {
visible: false
},
axisCrossingValue: 0
},
seriesDefaults: {
type: "column"
},
series: [{
field: "Connections",
categoryField: "Speaker",
color: function(point) {
var colors = ['#DE3C01', '#076EC4', '#E9D404', '#016E55', '#08428C', '#4F4F4F', '#B6B6B6'];
return colors[point.index];
}
}],
tooltip: {
visible: true,
format: "{0}%",
font: "1em Segoe UI",
template: "<img class='sessionSpeakerPhoto hidden-xs' src='#= dataItem.SpeakerImage#'> Session: #= dataItem.HubTitle # - #= dataItem.Connections#"
},
seriesClick: func.onSeriesClick,
dataSource: {
data: json
}
});

This creates the code at page load time. When a user moves to a room we log it into Azure. Then we send a SignalR message back down to the home page to tell the chart to refresh.

Wrap up​

I hope you found this blog useful and please don’t hesitate to ask any questions. If you want to know more about Kendo UI, the trail it’s downloadable here:​

DOWNLOAD KENDO


by Mark Jones via Everyone's Blog Posts - SharePoint Community

No comments:

Post a Comment