KEMBAR78
Sharepoint Framework Ui Customization Angular June-2017 | PDF | Share Point | Mobile App
0% found this document useful (0 votes)
91 views48 pages

Sharepoint Framework Ui Customization Angular June-2017

The document discusses the future of SharePoint development and the SharePoint Framework. The SharePoint Framework allows developers to use modern web technologies like Angular, React, and jQuery to build client-side extensions for SharePoint. It embraces open source tools and the web development stack. This will make it easier to develop for SharePoint and attract more web developers by using familiar skills. The framework also supports building extensions that work on both SharePoint Online and on-premises.

Uploaded by

Google Architect
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
91 views48 pages

Sharepoint Framework Ui Customization Angular June-2017

The document discusses the future of SharePoint development and the SharePoint Framework. The SharePoint Framework allows developers to use modern web technologies like Angular, React, and jQuery to build client-side extensions for SharePoint. It embraces open source tools and the web development stack. This will make it easier to develop for SharePoint and attract more web developers by using familiar skills. The framework also supports building extensions that work on both SharePoint Online and on-premises.

Uploaded by

Google Architect
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 48

Preparing Your Toolbox

for the SharePoint


© 2017 Progress. All Rights Reserved.

Framework
With Angular, Webpack and Kendo UI

WHITEPAPER
Table of Contents
Introduction / 4

Future of SharePoint Development / 4

Selecting our Web Stack and UI Library / 7

Building the App: Contract Register / 10

The App and the App Component / 13

The Contract List / 14

The Modal Service / 17

The Contract Form / 21

Validation / 24

PDF Generation / 26

The Contract Service / 27

PnP JavaScript Core Library / 28

Chaining Promises—Always be Making Promises / 30

Batching Requests, the Easy Way / 31


© 2017 Progress. All Rights Reserved.

Progress.com 2
Building the App: Behind the Scenes / 33

Typings / 34

Gulp / 35

Webpack Bundling / 39

Getting Started / 46

Summary / 46

About the Authors / 47


© 2017 Progress. All Rights Reserved.

Progress.com 3
Introduction Future of
This whitepaper discusses the future of SharePoint SharePoint
development and where Progress® Kendo UI® can
provide value in custom SharePoint application
development.
Development
We want to follow the trails of a SharePoint The future of SharePoint development and
developer, where we’ve been (with Full Trust, customization is the SharePoint Framework.
Sandbox, App/Add-In Web) and where we are
heading next with the open web stack. This was It is a client-side based framework that allows
an inevitable change that was happening with or JavaScript customizations to work on top of
without Microsoft, based on all client projects that SharePoint Online. It will also work with SharePoint
we see and on talking with the community at large. Server on-premises in a future update.

With the upcoming SharePoint Framework ("First If you think SharePoint and Microsoft have been
release this summer"), Microsoft is no longer standing still in the last few years—you might be
dictating our toolset, but embracing open web shocked. They have silently come a very long way!
technologies and wanting to support industry
trends as first class supported customizations within The Framework and Why it’s Awesome
SharePoint.
Firstly, it is a recognition that Microsoft should
There is a lot to catch up on with the SharePoint adopt and build on top of progressive web
Framework so we better get started! technologies. Instead of learning Microsoft’s version
of a technology that’s rapidly outdated, we can
Our main goals for this whitepaper are to: use the latest and greatest in jQuery, KnockoutJS,
AngularJS or ReactJS—even mix and choose
•• Get you excited about the new SharePoint technology based on our needs. These open web
Framework and related web stack technologies. technologies are all supported on SharePoint
Online.
•• Use a great set of web stack tools that work well
together to build a practical SharePoint business We are no longer asked to learn strange object
application that can get you started with these models or peculiar syntaxes like CAML or
technologies today. DisplayTemplates. Instead, we just need the
© 2017 Progress. All Rights Reserved.

common, modern web technology stack to be

Progress.com 4
effective and productive. source on GitHub.

For businesses, this means we can train our developers In addition, Microsoft is releasing a tool called the
in skills that are useful even outside of SharePoint. It is SharePoint Workbench, which will allow us to run
also far easier to hire web developers and have them and test our controls and applications within an
contribute to a SharePoint project quickly. offline environment so we can work on our projects

For traditional SharePoint developers, this means we


are required to familiarize ourselves with (yet again) a
new set of tools. This may be a big negative for some,
but we believe it to be a great opportunity. It will open
us up to a much broader world of web development
and at the same time open up SharePoint to a whole
world of web developers. Yes, we will have to step
up our game, but it is a small price to pay, compared
to the past when we had to hack around ancient,
unloved, SharePoint-specific technology.

Secondly, Microsoft promised that they will stop


"cheating" by creating backdoors for themselves.
This time, they are committed to only build on
top of this framework. Starting with the Office 365
Video Portal, the improved Delve Blogs and the new
Document Library experience are all the beginnings
of Microsoft’s own work building on top of the
SharePoint Framework. The upcoming new Team
Sites are the next step, along with several sneak
peeks of the publishing framework.

The Tools

The tools that Microsoft adopted for the SharePoint


Framework are best of breed tools from the web
world: Template and project generators from
Yeoman, build pipeline and tasks with Gulp and
© 2017 Progress. All Rights Reserved.

project bundling with Webpack—distributed open

Progress.com 5
and run automated tests when we are not connected All modern browsers are supported as first class.
to SharePoint Online.
And all the new experiences are designed to be
Open Source responsive and rescale accordingly to the UI—
whether it is desktop or mobile, across all platforms.
All these framework, libraries and new tools are
released Open Source. We can now take a copy of This matches very well with developments in the
the code, improve it and create a pull request to web world, with technologies like Cordova allowing
submit back to the community. web applications to run within native applications,
the SharePoint Mobile App was born.
There are also several community projects spinning
up around the official releases, adding capabilities, It promises a smooth interface, caching and seamless
libraries and components available to our toolkit. connection to Office Client Apps, OneDrive and the
Most recently, the release of the PnP-JS-Core library new Team Site experience.
provides a new wrapper around the modern REST
API that should help developers transition into client The Mobile App also had the promise that
side development for the SharePoint Framework. customizations on top of the SharePoint Framework
will run within the Mobile App. This icing on the cake
Not too long ago, SharePoint developers using open may yet prove to be the biggest selling factor to
source libraries like SPServices to build their web businesses. If we come to Microsoft for this ride and
parts and application were somewhat considered develop the "new way," we get the experience across
cowboys. Today, developers are being encouraged all mobile devices—free.
to use them as best practice.
Timeframe
Jeff Teper and the Focus on Modern and
Mobile The SharePoint Framework (SPFx) was in private
preview, and through the writing of this whitepaper
One big exciting development on the roadmap was has gone into developer preview and released to the public.
the return of Jeff Teper, the father of SharePoint,
back to lead SharePoint efforts within Microsoft.

The team had zeroed in on several key updates to


SharePoint.

While the backend of SharePoint Online and


© 2017 Progress. All Rights Reserved.

On-Premises continues to update forward in an


evergreen way, new experiences modernize the
SharePoint experience.

Progress.com 6
Update: Developer Preview (August 2016) run within SharePoint, or even in Office Add-Ins.

The Developer Preview, our first taste and our There are a multitude of scenarios that have taken
feedback to Microsoft, is in two areas. Firstly, us to this point. The next step is to evaluate the
Microsoft’s own guidance doesn’t cover Angular yet. components and technologies that are available to
We believe this to be crucial before public release as us and pick the ones that we will focus on.
a majority of full client-side applications developed in
the last few years are using the Angular framework. Next, we will discuss web technologies that we
believe align well with the direction in the open
Secondly, SPFx build Gulp tasks and Webpack web stack. We present this technology set as a
configurations are hidden from the user. This good solution that works really well, but keep in
hides some of the ‘magic’ but also makes the build mind many more technologies, e.g. KnockoutJS or
process difficult to customize for different teams. For ReactJS will also work fine. You should evaluate this
example, if we want to add more tasks to the build suggested list and make adjustments accordingly,
pipeline, we want to integrate a different testing relating to your team’s situation.
framework or even deploy the solution to existing
SharePoint 2013 On-Premises—the current Gulp Technologies
tasks hides that.
Node, NPM—In the JavaScript world, Node is the
But we are expecting public release to happen very runtime that lets us run code without a browser.
soon this year. And we hope to work with Microsoft The modern web stack runs on JavaScript—script
to address some of these issues. running in Node on the server and script running in
browser on the client.

Selecting Our NPM is like nuget for getting libraries and tools that
we need to run tools as well as libraries for the web

Web Stack and UI application. Historically, Bower was used for client-side
JavaScript libraries—technologies such as Webpack
module bundling means that there is no longer a real
Library distinction between what is a client-side library or
what is just a JavaScript library. If it’s needed on the
client side, the bundler will pack it for us.
Ready, Set, Go! Onward to the SharePoint
Framework AngularJS—Framework that helps us manage
databinding, components and composition. This is
© 2017 Progress. All Rights Reserved.

So, you are all set and excited about the SharePoint one of the most popular web frameworks that likely
Framework. You are looking to customize Office doesn’t need any introduction. Many companies have
365. You want to bring your customizations down to
SharePoint Mobile. You want to build apps that will

Progress.com 7
already invested skills and code to this framework. year and is rapidly gaining momentum. At its core,
its unique philosophy is to utilize parallel code
For this project, we pick Angular over React because splitting to load JavaScript quickly into the browser.
of our own experiences, having used Angular in And it is designed to not only minify and uglify
client projects in the past and the readily available JavaScript, but also able to generate source-maps
UI frameworks like Kendo UI that are optimized for and run a development Webpack server that could
Angular. recompile on the fly. Webpack is fairly opinionated
about how things should work, but has taken over
Yeoman—‘The web’s scaffolding tool for modern much of the functionality that used to depend on
webapps’ is one of the most exciting pieces to the several different tools—Browserify, Gulp and require/
SharePoint Framework puzzle. Before, we were systemjs. In the SharePoint Framework, Webpack will
dependant on Visual Studio Wizards to create the be one of the pieces used to package our resources
new SharePoint project. Now, we can generate the both to run in a local development server, or for
web project using the Yeoman generator running on deployment to a CDN to be used in SharePoint itself.
node.js from the command prompt.
Visual Studio Code—We choose to use Visual
Microsoft currently has an ongoing very successful Studio Code, as it’s a much simpler code editor.
Yeoman template for creating Office Add-ins VS Code doesn’t try to be a fully Integrated
called generator-office (yo office). The SharePoint Development Environment (IDE) with full SDKs and
Framework generator will be called generator- wizards. Instead, it tries to be a good code editor
sharepoint (yo sharepoint). that understands files grouped in folders really well.

Although we are highly anticipating the SharePoint Where does Kendo UI fit in and what are the
template, you won’t be restricted to it. You can make Alternatives?
your own adaptations and templates and share them
within your development team or with the entire If we are building business applications, we are
community. probably building forms and we need great controls
with out-of-the-box validation capabilities. Basic
For our project, we will use the PnP JSCore Yeoman controls like textbox, text area and date pickers are a
Template also released by Microsoft to get started. given. As we consider additional controls like grid view,
rich text editing and multi-lingual support for global
Gulp—Task runner, build, test, deploy to SharePoint.
Traditionally, Gulp takes a more active role in minifying
and concatenating files before deployment. That task
has largely been taken over by Webpack. Gulp remains
© 2017 Progress. All Rights Reserved.

useful in our stack because it is still more flexible as a


task runner to connect different tasks, and ultimately,
manage the deploy tasks to SharePoint.

Webpack—It’s one of the most interesting pieces


of the modern web stack that appeared in the last

Progress.com 8
customers, using a UI framework is a no brainer. Kendo UI Professional is next step with 70+
advanced controls (grid, spreadsheet, Gantt
A Grasp of What’s Out There and Our and scheduler being our recent favorites). While
Experience with it: Kendo UI Pro isn’t open source, we do get access
to dedicated support and full source code to
•• UI Bootstrap—is well tested and used with review so we can still take a crack at a particular
Angular, but not particularly optimized for problem by reading the source code ourselves.
SharePoint or Office 365. You may have some It’s the best of both worlds.
growing pains as you adjust CSS and loading
orders to get UI Bootstrap to work nicely. Kendo UI controls support jQuery, Angular 1.x and
Bootstrap’s Responsive CSS doesn’t work with Angular.
SharePoint MasterPage out of box, so you will
need to tinker with that. When single page React support is coming soon.
applications (SPAs) are supported properly with
SharePoint Framework, this may change. All these libraries will work with any SharePoint and
Angular projects and you don’t have to use Kendo
•• Office UI Fabric Core, Components and UI. Our example does, but it can be replaced with
ngOfficeUIFabric—Office UI Fabric is a set of ngOfficeUIFabric.
styles and components provided and used by
Microsoft to build their own customizations. There are plenty of things to learn and get going
The focus is Office 365 friendly CSS, and in the new framework—spending time on making a
components designed for plain JavaScript control work is probably less important in the grand
or React. As Microsoft doesn’t provide an scheme of things.
Angular implementation, ngOfficeUIFabric is a
community attempt to create native wrappers for
Office UI Fabric components, but along with the
components themselves they are not yet widely
adopted and you may find support difficult to
come by. UI Fabric Core is mainly supported for
SharePoint Online, Office Add-Ins and latest “I have some first-hand experiences here
versions of SharePoint 2013/2016. [at a client] customizing both UI-Bootstrap
and ngOfficeUIFabric on several client
•• Kendo UI comes in three plans—Core, projects. Halfway through each project I’m
Professional and Complete, where Complete thinking, maybe I should have just gone for
goes beyond the scope of this project as it a commercial supported framework and not
© 2017 Progress. All Rights Reserved.

includes additional libraries such as wrappers spend all this time making controls work.”
for MVC, JSP and PHP. Kendo UI Core is free,
open source and community-supported.

Progress.com 9
Building the App: Contract
Register
We want to demonstrate how the technologies On a functional level, the application should:
discussed in the previous section work together and
build a simple yet useful application: The Contract • Show a list of all contracts and status (the
Register. register)
• Allow for adding, editing and viewing contracts
Use Case • Signed contracts should be uploaded and stored
within the register
When a government department shares information
with a third party, which is not covered by any High Level
other legal agreement, they require the information
receiving party to sign a contract. What are we building? Below is a high-level diagram
of the components. Each component isn’t a lot
of code as we aim to show a simple, but working
A ‘System Access Agreement’ describes a code of system and how each component interacts.
conduct for external parties to access any of the
department’s information systems and the general
terms of usage.
© 2017 Progress. All Rights Reserved.

Progress.com 10
Group Artefacts by Function—Not by Type Files organized by function, with views next to their
controllers.
In many starter examples of Angular projects, the
SPA is grouped by type of the object. Whether it is See Todd Motto’s Angular Style guide for in-depth
a View, Model or Controller—they are often lumped examples: https://github.com/toddmotto/angular-
and grouped in separate directories. You see a styleguide
component like DataService fairly regularly as the
only component to talk to the backend.

What many people later find out is that while that


sort of grouping makes a good simple starter
project or learning demo, it is actually not a great
way to keep related functions together. When you
are working on the controller for a contract-list, you
probably want the contract-list view template right
next to it.
© 2017 Progress. All Rights Reserved.

Progress.com 11
Entry – index.js

Every application has a starting point. Ours is


index.js. Here we defined our Angular single page
application, ‘app’. It has dependency on Kendo UI
Angular Directives. It has four parts—a contract
service where we will talk to the backend system (in
this case, SharePoint), a contract list, a contract form
and the top level ‘app component’ which is the root
component that contains everything inside.

var app = angular


.module(‘app’, [‘kendo.directives’, ‘kendo.window’]);

app.service(‘contractService’, ContractService);

app.component(‘app’, App);

app.component(‘contractList’, ContractList);

app.component(‘contractForm’, ContractForm);

The base HTML page that we bind to is SPApp.


html which is generated from sp-app.ejs file via the
Webpack HtmlWebpackPlugin. We discuss this in a
later section. The HTML is similar to index.html.

<div ng-app="app">

<app>Loading...</app>

</div>
© 2017 Progress. All Rights Reserved.

This is where we bootstrap our Angular application


and load the root App component.

Progress.com 12
The App and the App Component
The App component is loaded in the default page Webpack excels at loading and packing HTML as
replacing the <app> element. string into a single JavaScript file. So one of the early
choices we end up with is to rely on Webpack to do
Here we see a simple example of Angular 1.5’s new templating, and essentially do away with Angular
components syntax, combining best practices of Template Cache.
directives with isolated scopes and controllerAs
$ctrl by default, forward looking towards a web- Note: Should you still want to use Angular Template
components future in Angular 2.0. Cache, there is a Webpack ng-cache loader that will
take template files and preload them so they are still
<contract-list></contract-list> available via the templateUrl property.

module.exports = { We use template string consistently through the


template: require(‘./App.html’), project.
controller: App
};

function App() {
// App is just a top level component
}

A component in Angular can reference either


templateUrl. We then need to pass the template as a
URL or preload it into the Angular Template Cache.
With the template option, we then need to specify
inline HTML string.
© 2017 Progress. All Rights Reserved.

Progress.com 13
The Contract List
Our first interesting component is a list of the
contracts we want to show in the application.
The template will look extremely simple.

<h1>Contract Register</h1>

<kendo-grid options="$ctrl.gridOptions"></kendo-grid>

Note that we wanted to use the Kendo UI Grid


control for some fairly sophisticated functions.
The Kendo UI Grid control is part of the Kendo UI
Professional pack. A simpler example we started with
uses the Kendo ListView control, which is part of
the Kendo UI Core pack, and works very well for list
rendering.

Kendo UI controls follow a more traditional way


where we bind the data to a Kendo UI DataSource
object first. The data source object tracks paging,
filters and grouping, and feeds that information to
© 2017 Progress. All Rights Reserved.

one or more controls on the page (e.g. a Grid and a


Pager).

Progress.com 14
Angular 1.5 Controller

module.exports = {
template: require(‘./contractList.html’),
controller: ContractListController
};

ContractListController.$inject = [‘$scope’, ‘$element’, ‘$attrs’, ‘contractService’,


‘$kWindow’];
function ContractListController($scope, $element, $attrs, contractService, $kWindow) {
var $ctrl = this;

$ctrl.source = null;
$ctrl.openFormWindow = openFormWindow;
$ctrl.refresh = refresh;
$ctrl.newContract = newContract;

activate();

...
}

The ControllerList is written with readability in mind. code to keep the code simple. (Previously before 1.5,
We follow several best practices from Todd Motto’s we used vm from our MVVM background).
excellent Angular Style Guide. We list controller
members near the top, and initialize them with Because Webpack will take care of bundling and
activate() method. uglification of the parameters, use $inject header to
tell Angular which arguments need to be provided
In Angular 1.5, the controllerAs is on by default, and by dependency injection.
the default name is "$ctrl" so we reflect that in the
© 2017 Progress. All Rights Reserved.

Progress.com 15
The Controller activate()

function activate() {
$ctrl.source = new kendo.data.DataSource({
data: [],
schema: {
model: {
fields: {
...
}
}
}
});

$ctrl.gridOptions = {
dataSource: $ctrl.source,
sortable: true,
filterable: true,
groupable: true,
columns: [
...
],
toolbar: [
{
name: "add",
text: "New Contract",
template: ‘<a ng-click="$ctrl.newContract()" class="k-button k-button-icontext
k-grid-add" href="\\#">New Contract</a>’
}]
};

$ctrl.refresh();
}
© 2017 Progress. All Rights Reserved.

We set up the Kendo UI data source. And finally, we call refresh on the list. Refresh is a
method we will come back to later. It is exposed as a
We set up grid options for the Kendo Grid public method on the controller.
component.

Progress.com 16
The Controller refresh()

function refresh() {
contractService.getItems().then(function (items) {
$ctrl.source.data(items);
});
}

The refresh method asks the contract-service to


get a list of items then bind that list to Kendo UI
DataSource’s data member.

Kendo UI’s datasource is an observable array, and it


will work with Angular to correctly refresh the grid
list.

The Modal Service


When we click on a form we want to bring up, the
form in a modal dialog, the Angular way.
© 2017 Progress. All Rights Reserved.

Progress.com 17
Control Approach: Bootstrap’s $modal, Office
UI Fabric UIF-Dialog, Kendo UI Kendo-Window

All component libraries for a pop up window


(dialog) have simple syntax designed for opening an
individual dialog. This pattern is useful for scenarios
to create dialogs to ask for confirmation, or to bring
up a terms and conditions when a user visits an
intranet site.

The control-based approach is not a suitable


pattern when you want to bind different views and
controllers to different items in a set of items.

The control approach also usually does not support


multiple stacks of window dialogs. So we couldn’t
pop up a window dialog and have that window
dialog pop up yet another confirmation dialog in a
well-supported way.

Factory Approach: SharePoint SP.UI.


ModalDialog, UI Bootstrap $modal, $kWindow

Both the UI Bootstrap library and Kendo UI have


implementation and examples for a factory-based
approach. Incidentally, even SharePoint’s own SP.UI.
ModalDialog works like a service and lets us create
dialog windows and stack them.

Here is a method to open a contract form


component within a popped up dialog window.
© 2017 Progress. All Rights Reserved.

Progress.com 18
function openFormWindow(dataItem) {
// http://plnkr.co/edit/PjQdBUq0akXP2fn5sYZs?p=preview
var windowInstance = $kWindow.open({
options: {
modal: true,
title: dataItem.Title,
resizable: true,
visible: false
},
template: ‘<contract-form contractid="$ctrl.contractid"
$close="$close(result)" $dismiss="$dismiss(reason)"></contract-form>’,
controller: [‘contractid’, function (contractid) {
var $ctrl = this;
$ctrl.contractid = contractid;
}],
controllerAs: ‘$ctrl’,
resolve: {
contractid: function () {
return dataItem.Id;
}
}
});

windowInstance.result.then(function (result) {
if (result) {
$scope.result = ‘confirmed!’;
$ctrl.refresh();
}
else {
$scope.result = ‘canceled!’;
}
});
}
© 2017 Progress. All Rights Reserved.

Progress.com 19
The $kWindow service lets us open a window, and More information is available on https://angular-ui.
at the same time bind an Angular template and a github.io/bootstrap/#/modal.
controller to this template. The service also attaches
$close and $dismiss methods to the controller for Kendo UI window-service is available here and the
the form. So the child component can control when code itself is on GitHub.
it is ready to be closed.
One specific implementation note—because our
Angular’s template binding kicks in and allows the project utilizes Webpack bundling, we do not have
contract-form component to be rendered within the the template ‘window.html’ which is needed by
popped up modal dialog. angular-kendo-window service. Instead, we bundle
the content of window.html via a template string in
Finally, the service returns an instance that has our implementation.
a result promise that can be used by the parent
controller to watch for the result of the opened
dialog window.

kwin.directive(‘kWindowFrame’, [‘$kModalStack’, ‘$q’, ‘$animate’, ‘$injector’,


function ($modalStack, $q, $animate, $injector) {
... snipped
return {
scope: {
index: ‘@’
},
replace: true,
transclude: true,
template: ‘<div kendo-window="myKendoWindow" k-options="options" modal-
render="{{$isRendered}}" tabindex="-1" role="dialog" > <div><div k-window-
transclude></div></div> </div>’,
/*
templateUrl: function (tElement, tAttrs) {
var windowInstance = $modalStack.getTop().value;
return windowInstance.windowTemplateUrl || ‘window.html’;
},
*/
© 2017 Progress. All Rights Reserved.

... snipped
};
}])

Progress.com 20
The Contract Form
The Contract Form is our third component in the
application. It is in many ways similar to the contract
list component. Because it is loaded in a dialog
window, it also has unique bindings to the dialog
window’s $close and $dismiss methods.

module.exports = {
template: require(‘./contractForm.html’),
controller: ContractFormController,
bindings: {
‘$close’: ‘&’,
‘$dismiss’: ‘&’,
‘contractid’: ‘<’
}
};

The contractid is a property provided by the


contract list when the contract form is opened.
The contract form’s activate method requests
the contract from the contract-service.

activate();

function activate() {
//ID is known get item refresh
if($ctrl.contractid){
contractService.getItem($ctrl.contractid).then(function (item) {
$ctrl.item = item;
});
}
}
© 2017 Progress. All Rights Reserved.

Progress.com 21
The contract is set to the controller’s item property. This is then bound to the template for contract form.

<fieldset>
<legend>Contract Details</legend>
<p class="forms">
<label>Contract Type</label>
<select kendo-drop-down-list style="width: 100%;" ng-model="$ctrl.item.Title">
<option value="SAA" selected>System Access Agreement</option>
<option value="NDA">Non Disclosure Agreement</option>
</select>
</p>
<p>
<label>System Name</label>
<input type="text" class="k-textbox" ng-model="$ctrl.item.SystemName" required />
</p>
<p>
<label>Give a detailed description of the system </label>
<textarea ng-model="$ctrl.item.InformationDescription" required></textarea>
</p>
<p>
<label>End Date</label>
<input kendo-date-picker ng-model="$ctrl.item.ContractEndDate" required />
</p>

</fieldset>
© 2017 Progress. All Rights Reserved.

Progress.com 22
Kendo UI controls here can be bound simply to the model on the controller.

<p>
<button kendo-button type="submit" ng-click="$ctrl.save()">Save</button>
<button kendo-button type="button" ng-click="$ctrl.dismiss()">Cancel</button>
</p>

function save() {
... snip
//ID is known update item
if ($ctrl.contractid) {
contractService.updateItem($ctrl.item).then(function (item) {
$ctrl.close();
});
}
else {
//no ID add as new item
contractService.newItem($ctrl.item).then(function (item) {
$ctrl.close();
});
}
}

function close() {
$ctrl.$close({
result: ‘save’
});
}

function dismiss() {
$ctrl.$dismiss({
reason: ‘cancel’
});
}
© 2017 Progress. All Rights Reserved.

Progress.com 23
The buttons at the bottom of the form can also be item. This lets the UX bind to the latest version of
bound to methods on the controller. When invoked, the object, and if we are using calculated fields in
they call the contract-service to update or create SharePoint, this is necessary to bring back the server
new contract item. When that’s successful, they updates during the save.
call the $close or $dismiss methods on the dialog
window to close down the dialog, disposing the form Validation
component.
We bind any validation messages to the bottom of
If we don’t want to have save() close the modal the form. Kendo UI validation is very simple to set
window, then we need to take the saved item object up.
from the resolved promise and assign it back to $ctrl.
© 2017 Progress. All Rights Reserved.

Progress.com 24
Fields that are required, add a required attribute. Specify a kendo-validator in the form, and attach validate
method to the submit event of the form. (This is triggered by the save button, which is a type="submit").

<form kendo-validator="$ctrl.validator" ng-submit="$ctrl.validate($event)">

<fieldset>
<legend>Contract Details</legend>
<p>
<label>System Name</label>
<input type="text" class="k-textbox" ng-model="$ctrl.item.SystemName"
required />
</p>
<p>
<label>Start Date</label>
<input kendo-date-picker ng-model="$ctrl.item.ContractStartDate" required
/>
</p>
</fieldset>
<p>
{{ $ctrl.validationMessage }}
</p>

</form>

function validate(event) {
//block submit from making a postback
event.preventDefault();

if ($ctrl.validator.validate()) {
$ctrl.validationMessage = "";
$ctrl.validationClass = "valid";
} else {
© 2017 Progress. All Rights Reserved.

$ctrl.validationMessage = "There is invalid data in the form.";


$ctrl.validationClass = "invalid";
}
}

Progress.com 25
To be fair, UI-Bootstrap also has similar validation capabilities. Office UI Fabric components does not currently
have validation, but it is on the roadmap in the future.

function save() {
if (!$ctrl.validator.validate()) {
$ctrl.validationMessage = "There is invalid data in the form.";
$ctrl.validationClass = "invalid";
return;
}
//ID is known update item
if ($ctrl.contractid) {
contractService.updateItem($ctrl.item).then(function (item) {
$ctrl.close();
});
}
else {
//no ID add as new item
contractService.newItem($ctrl.item).then(function (item) {
$ctrl.close();
});
}
}

Update contract-form’s save() method—it should check and make sure the form is valid.

PDF Generation

The application uses Kendo UI to save DOM to PDF.


© 2017 Progress. All Rights Reserved.

Progress.com 26
function generatePdf(selector) {
kendo.drawing.drawDOM($(selector)).then(function (group) {
kendo.drawing.pdf.saveAs(group, "contract.pdf");
});
}

When the Generate Contract is clicked, the click


event calls a Kendo function to generate the current
DOM into PDF for download.

An alternative scenario may be to write the PDF


binary directly to SharePoint’s document library.

The Contract Service


The contract service is injected by most of the fields. You may consider moving this into a separate
application components and exposes the methods application component (handling configuration)
for talking to the SharePoint list backend. altogether.

ContractListController.$inject = [‘$scope’, The contract services use the methods provided by


‘$element’, ‘$attrs’, ‘contractService’, the PnP SharePoint JavaScript library to perform
‘$kWindow’]; actions in SharePoint using simple and clean looking
code.
In our example project, we also have the contract
service provisioning the Contract Register List and
© 2017 Progress. All Rights Reserved.

Progress.com 27
contractService.js

module.exports = ContractService;

var pnp = require(‘sp-pnp-js’);

//added here and as webpack external to prevent compile warnings


var _spPageContextInfo = require(‘_spPageContextInfo’);

//Use angular promises


ContractService.$inject = [‘$q’];

function ContractService($q) {
var self = this;

self.getItems = getItems;
self.newItem = newItem;
self.getItem = getItem;
self.updateItem = updateItem;

function getItems() {
return $q(function (resolve, reject) {
...
}

PnP JavaScript Core Library

What is this library? Currently, it contains a fluent SharePoint REST API, developers still need to know
API for working with the full SharePoint REST API how to complete basic operations with the REST
as well as utility and helper functions. This takes the endpoints to take advantage of all the options
guess work out of creating REST requests, letting available.
developers focus on the what and less on the how.
(Source: PnP JS Core Wiki) Together with the PnP JS Core API documentation,
performing list operation in SharePoint has never
© 2017 Progress. All Rights Reserved.

Even though using the library simplifies working been this easy.

Progress.com 28
Let’s compare creating a SharePoint list through a
‘normal’ jQuery (ajax) SharePoint REST API call vs
the PnP JS Core Library:

Using jQuery, the web request would look something


like this:

jQuery.ajax({
url: "http://<site url>/_api/web/lists",
type: "POST",
data: JSON.stringify({
‘__metadata’: { ‘type’: ‘SP.List’ },
‘Title’: ‘My List Title’,
‘Description’: ‘My list description’,
‘BaseTemplate’: 100,
‘AllowContentTypes’: false,
‘EnableVersioning’: true,
}),
headers: {
"accept": "application/json;odata=verbose",
"content-type": "application/json;odata=verbose",
...
}).then(..

Making this same list configuration call through the PnPJSCore add method would look like:

var additionalSettings = { EnableVersioning: true }


pnp.sp.web.lists.add(‘My List Title’, ‘My list description’, 100, false, additionalSettings).
then(..

As you can see this PnP example is much leaner and be added to the list creation request body. For
readable. The add method parameters allow us to finding all additional settings, we would still rely on
© 2017 Progress. All Rights Reserved.

specify the required properties to create the list with documentation available around the SharePoint
one line of JavaScript. REST API like the available list properties.

Additional settings like ‘EnableVersioning’ can be


passed in as an object of key value pairs and will

Progress.com 29
Chaining Promises—Always Be Making
Promises

Continuing to look at our contract service code, it


will probably not come as a surprise that all methods
return promises and often chaining promises within.

Where possible, we use Angular promises ($q), we


use $q() as a promise constructor and return it
directly without creating a deferred object first.

The PnP JS Core library uses ES6 Promises and


requires a polyfill for Internet Explorer to work.

function getItems() {
return $q(function(resolve,reject){
getList().then(function (list) {
list.items.get().then(function (items) {
resolve(items);
});
}).catch(reject);
});
}

function getList() {
return $q(function (resolve, reject) {
pnp.sp.web.lists.ensure(‘ContractRegister’, ‘’).then(function (result) {
if (result.created) {
setupContractRegister(result.list).then(function (list) {
resolve(list);
});
} else {
resolve(result.list);
}
© 2017 Progress. All Rights Reserved.

});
}).catch(reject);;
}

Progress.com 30
All of the contract service methods: getItems(),
newItem(), getItem(), updateItem() use the getList()
method to get the contract register list instance first.

The getList() method uses the ensure() method to


detect whether the register list already exists, and if
not creates it right away. Note: the ensure methods
do not update the list settings if the list already
exists.

The Lists.ensure() method is a wrapper around the


Lists.getByTitle() method, and when fails, catches
the error and calls Lists.add(). This event will be
logged to the console like:

The ensure method returns a promise that once


completed resolves an object containing the list
instance and the list creation data.

In the contract service when the list is created we Batching Requests, the Easy Way
call the setupContractRegister() method only once.
Although batching has been available through the
Any consecutive call of the ensure method will SharePoint Online REST API for some time, making
effectively be the same as calling the getByTitle () batching request work was a fairly complicated task.
method directly. Batching was added in the PnPJSCore 1.0.2 release,
the announcement has some great examples for
batching (as well as caching). It is important to note
that batching is not supported on SharePoint 2013
On-premise as of yet.
© 2017 Progress. All Rights Reserved.

Even though the PnPJSCore library makes batching


very easy, it is recommended to have a read of
Andrew Connell’s excellent series, SharePoint REST
API Batching—Understanding Batching Requests, to
understand the inner workings.

Progress.com 31
In our contract service, we use batching to provision the list fields for the contract register.

function setupContractRegister(list) {
return $q(function (resolve, reject) {
// create batch
var batch1 = pnp.sp.createBatch();
$q.all([
//Information System Details fields
list.fields.inBatch(batch1).addText(‘SystemName’),
list.fields.inBatch(batch1).addMultilineText(‘InformationDescription’, 8, false),
list.fields.inBatch(batch1).addNumber(‘InformationSensitivity’),
...
]).then(function () {
// add some demo entries
$q.all([
list.items.add({‘Title’: ‘SAA-001’, ‘ThirdPartyContactFullName’: ... }),
list.items.add({‘Title’: ‘SAA-002’, ‘ThirdPartyContactFullName’: ... }),
list.items.add({‘Title’: ‘SAA-003’, ‘ThirdPartyContactFullName’: ... })
]).then(function () {
resolve(list);
});
});

batch1.execute();

});
}

As explained in the version 1.0.2 announcement, the get the benefits of REST batching. When we want
same applies here. The key thing is we can keep to add the list fields without batching, (for example
chaining our promises or add them to an array of to make this work on-premises) we would need to
$q.all([ … ]) promises and they won’t be resolved chain them one after the other. Calling them in parallel
© 2017 Progress. All Rights Reserved.

until the batch execute command is called. (array of promises) will return conflict errors as list
configuration can’t be changed by multiple requests
We can almost write the same code as before and at the same time.
just add in the inBatch() method to the chain and

Progress.com 32
This is different for list items as they can be added
simultaneously as the above method demonstrates.
This does however result in three separate http
requests, where if we would have used batching
there would have only been one. “You might not be cooking in the SharePoint
Dev Kitchen, but that really doesn’t stop you
from watching and learning how to use the
same tools to cook at home.”

Building the
App: Behind the
Scenes
There’s always two sides to a great meal—there
is the recipe and there are the tools that do the
cooking. AngularJS, Kendo UI and PnP JS Core work
well to create the application. And then we look
to the build side—which tools do we use to really
improve the build experience.
© 2017 Progress. All Rights Reserved.

Progress.com 33
Typings
Intellisense is for Everyone, Not Just
TypeScript

With Visual Studio Code, detailed intellisense can be The default jsconfig.json file essentially tells Visual
enabled for both TypeScript and JavaScript projects. Studio Code what EMCAScript level we are running,
In a TypeScript project, the settings would be stored and which directories to ignore parsing (node_
in a tsconfig.json file. For a plain JavaScript project, modules) as that would make the editor unbearably
we need to add a jsconfig.json file. slow.

https://code.visualstudio.com/docs/languages/ Typings have more benefits in a TypeScript project


javascript#_javascript-projects-jsconfigjson as TypeScript encourages developers to strongly
type their code, and the intellisense service is more
The typings module with the configurations in intelligent. But even in a plain old javascript project,
typings.json helps the project track the latest Type Typings is useful and gives better information than
definition files available for modules within the VS Code is able to guess.
JavaScript project. Visual Studio Code is then able
to use the TypeScript definition files to provide code We utilize PnP JS Core to talk to SharePoint. It is built
intellisense service. with typings definitions, which makes intellisense
fairly robust.
© 2017 Progress. All Rights Reserved.

Progress.com 34
Gulp
Gulp is our chosen build system—a Gulp script is
JavaScript script. It makes it easier to specify how we
want to chain our operations or sometimes dive into
a few simple functions. Various sets of our tools work
with Gulp as Gulp extensions.

The entire build process runs on Gulp tasks, these


are defined in gulpfile.js

gulp.task("build", ["lint", "webpack:build"]);

gulp.task("lint", () => {
return gulp.src("./src/**/*.js")
.pipe(eslint())
.pipe(eslint.format());
});

gulp.task("webpack:build", function(callback) {
// run webpack
webpack(config, function(err, stats) {
if(err) throw new gutil.PluginError("webpack:build", err);
gutil.log("[webpack:build]", stats.toString({
colors: true
}));
callback();
});
});

Traditionally, Gulp would be used to trans-compile,


minify, concatenate and uglify JavaScript files, but it
relies on other modules to do this, and Webpack has
© 2017 Progress. All Rights Reserved.

taken over most of the responsibility.

Visual Studio Code understands Gulp and will


happily run the Gulp tasks that we define.

Progress.com 35
We can also type ‘task ‘ to bring up a list of other Gulp tasks that can be run from Visual Studio Code.
© 2017 Progress. All Rights Reserved.

Progress.com 36
gulp-eslint - eslint Checks the JavaScript Within Visual Studio Code, an extension is available
for eslint so we can get syntax checking while we
Particularly, we are looking for areas where a variable work on each file.
might be undefined. This is an extremely common
issue where perhaps we’ve used the wrong casing, gulp-spsave
or we had renamed the variable, but had leftovers,
or—and everyone’s guilty of this—we’ve copied code To really simplify getting our result files into
from the Internet (or from another function) and SharePoint, a few Gulp tools have been built. The
haven’t gone through and renamed everything. most common are gulp-spsync and gulp-spsave.

Another error that eslint can pick up is when we’ve Gulp-spsync is designed for SharePoint Online, but
had mismatched brackets or braces. Usually, an has been forked to gulp-spsync-withcred that lets us
advanced editor like VS Code will pick this up, but use it for both Online and On-Premises.
eslint will double check it for us.
Gulp-spsave is designed for both Online and On-
There are some best-practice rules, such as no-alert, Premises. The configuration options for both tools
where it will warn us if we’ve left alert() calls for are similar and they work similarly. Gulp-spsave has
testing behind and it’ll end up in production. a slight edge on the number of options as well as a
rewritten stack in gulp-spsave that uses sp-request
On style issues, we are lenient on issues related to module; it copies files faster.
linebreak characters, tabs and spaces. But on larger
teams this may be dictated to avoid edit wars and
huge merge nightmares.

Take particularly single or double quotes—we lean


on using double quotes for all strings, as the JSON
specification explicitly says only double-quotes can
be used for member definitions.

We integrate eslint within Gulp as a lint task, running


the extension gulp-eslint
© 2017 Progress. All Rights Reserved.

Progress.com 37
We started our tools on gulp-spsync, then to gulp-
spsync-withcred so we can have one tool for both
Online and On-Premises deployments. Finally, we
moved over to gulp-spsave because it had more
options and runs faster.

var gulp = require("gulp"),


gutil = require("gulp-util"),
o365 = require("./o365-user.js"),
spsave = require(‘gulp-spsave’);

gulp.task("deploy", ["webpack:build"], function(){

return gulp.src(‘./dist/SiteAssets/**/*’)
.pipe(spsave({
"username": o365.username,
"password": o365.password,
"siteUrl": o365.site,
"folder": ‘SiteAssets’
}));
});

gulp-spsync SPO https://github.com/wictorwilen/gulp-spsync


gulp-spsave SPO, On-Premises, *fast* https://github.com/s-KaiNet/gulp-spsave

gulp-spsync-cred SPO, On-Premises https://github.com/estruyf/gulp-spsync-creds

robocopy Require mapped drive


© 2017 Progress. All Rights Reserved.

Progress.com 38
Webpack Bundling
Running Webpack in Development Mode external modules. In this configuration, the SPApp.
html is generated and points to reference vendor
In development, our solution is to use jQuery, libraries in a CDN.
Angular and Kendo UI (JavaScript and CSS) as

<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2016.2.714/styles/kendo.common-


office365.min.css" />
<link rel="stylesheet" href=" https://kendo.cdn.telerik.com/2016.2.714/styles/kendo.office365.min.
css" />
<script src="https://kendo.cdn.telerik.com/2016.2.714/js/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2016.2.714/js/kendo.all.min.js"></script>

<script src="../SiteAssets/vendor.js"></script>
<script src="../SiteAssets/app.js"></script>

<div ng-app="app">
<app>Loading...</app>
</div>

Additionally, we upload local fallback versions in case This means that Webpack is packaging only a
the CDN is unavailable. vendor.js file for some remaining libraries we use, as
http://docs.telerik.com/kendo-ui/intro/installation/ well as the app.js containing all the application code
cdn-service that we wrote, along with the source map files for
these.
© 2017 Progress. All Rights Reserved.

The files are pushed to SharePoint.

Progress.com 39
Now, the HTML page can’t run directly in SharePoint Instead, the HTML can be included into a blank
Online. SharePoint Online will actually not serve page and run via the Content Editor webpart.
HTML pages; it will attempt to download them to
the computer.
© 2017 Progress. All Rights Reserved.

Progress.com 40
This is a one-time setup so we didn’t include this in changes in this area as SharePoint Framework is
the Gulp scripts. released.

In the future, with the SharePoint Framework, we If we look at the network tab in the browser’s F12
expect there will be a Gulp task that will deploy Development Mode, we can see a clearer picture of
artefacts to SharePoint as well as register them where the files are being loaded from.
with SharePoint APIs. We expect there will be small

The Kendo UI CSS, jQuery, Angular and Kendo JS Webpack packs everything into modules—a highly
files are loaded from CDN. Then a single vendor.js is unreadable mess, as one would expect.
loaded (this is where we packed additional vendor
libraries into a single file) along with our app.js file—
it is packed to only 17kb.
© 2017 Progress. All Rights Reserved.

Progress.com 41
But a quick Pretty-Print will help make more sense of it.

Here – module 6 looks like our contract service.

And if we toggle the Source Map option in our browser debugger:

We are now able to set break points and debug our JavaScript even though it is minified in
SharePoint.
© 2017 Progress. All Rights Reserved.

Progress.com 42
Note: We can map the correct lines and set breakpoints, but remember that local variables have been
renamed to simple "e", "t" variable names. While we may be expecting "items", it is actually "e" that we should
be looking for.

Running Watch Mode

The gulp-spsave documentation already gives an example of how to use a Gulp task to monitor file changes
and upload them automatically to SharePoint. This will save us from manually having to run the build/deploy
task again. Since we use Webpack to bundle our files, we combine webpack’s watch mode with the gulp-
watch plugin to upload changes.

gulp.task("webpack:build-dev-watch", function () {
//set webpack watch
var devWatchConfig = Object.create(devConfig);
devWatchConfig.watch = true;
// run webpack
webpack(devWatchConfig, function (err, stats) {
if (err) throw new gutil.PluginError("webpack:build-dev-watch", err);
gutil.log("[webpack:build-dev-watch]", stats.toString({
colors: true
}));
});
});

gulp.task("deploy-watch", ["webpack:build-dev-watch"], function () {


//need awaitWritefinish to prevent uploading twice
https://github.com/paulmillr/chokidar#api
watch(‘./dist/SiteAssets/**/*’, { awaitWriteFinish: true })
.pipe(spsave(spSaveConfig));
});

The ‘webpack:build-dev-watch’ tasks sets ‘watch debugging experience. The disadvantage here is
© 2017 Progress. All Rights Reserved.

= true’ in the Webpack config. In addition, the dev that the output file sizes will be larger, so depending
build doesn’t minify and uglify the JavaScript, which on the size and upload speed, we may want to use a
speeds up the compile time and gives a better minified/uglified build and rely on the source map.

Progress.com 43
In watch mode (and caching enabled) Webpack Webpack automatically watches the source directory
keeps each module in memory and only recompiles we are working in, so if we update and save any
changed output files. Since all our source files are of our work, Webpack will automatically rebuild
bundled in the app.js file, any change to, for example, the bundled modules and serve them all from
index.js or contractservice.js will trigger Webpack to memory, without need to write to file, then upload
generate a new app.js, but it won’t touch vendor.js or to SharePoint. This gives us extremely fast testing
app.css files (if nothing has changed in the related cycles.
files of course).
Webpack-dev-server has an automatic refresh
The Gulp file watcher monitors the "/dist/SiteAssets/" option with --inline mode, so when Webpack detects
folder for new changes that are then uploaded by a change within the source code, it will even trigger
spsave every time Webpack rebuilds them. the SharePoint page to refresh.

Calling the ‘deploy-watch’ task will build all the In the SharePoint Framework, Microsoft talks of a
output files once, upload them and after that only SharePoint Workbench tool that will allow us to test
upload changes. our customizations ‘offline.’ We expect the webpack-
dev-server to be a related part of this offline story.
Running Webpack in Memory—the Webpack
Dev Server

Webpack has a webpack-dev-server mode that will


run and serve Webpack assets directly. What this can
let us do is instead of injecting the app.js and vendor.
js into the HTML page and upload it to SharePoint,
we can inject localhost references to app.js and
vendor.js, and have that uploaded to SharePoint.

Locally, the browser will connect the scripts served


from webpack-dev-server with other scripts from
SharePoint Online.
© 2017 Progress. All Rights Reserved.

Progress.com 44
Running Webpack Dev Server with Hot For example, if we aren’t using Kendo UI ListView
Module Replacement control it doesn’t get bundled in the vendor.js file,
making a custom vendor file that’s a lot smaller.
The ultimate craziness in the Webpack world is hot
module replacement. Because Webpack loads each In practice, we find the bundling more difficult to
dependency as a separate module, it is able to watch work with when we are testing or debugging issues
for changes to individual modules and replace them with the controls. So while this is an exciting option,
at runtime. it should be reserved only for production code.

As we make a change in our file, Webpack rebuilds


the package and then replaces the module that’s
currently loaded in our webpage with the new code.

Because our HTML and CSS assets are also loaded


into script by Webpack, it can change the template
that’s used to generate and bind HTML in the
application too.

All this can happen without us having to refresh the


browser; this allows JavaScript current scope to be
maintained as the code is switched out.

Running Webpack for Production

For production, Webpack excels at packaging and


removing modules from bundles that we aren’t
actually using. For both Angular, ngOfficeUIFabric
and Kendo UI, as these libraries are built from
components, Webpack bundling can actually detect
which modules we are actually using within our code
and will strip out modules that aren’t used by us.
© 2017 Progress. All Rights Reserved.

Progress.com 45
Getting Started Conclusion
How to run the Contract-Register demo These are the most exciting times as a developer,
1. You will need to have NodeJS installed especially for a SharePoint developer. There are
2. Fork from https://github.com/johnnliu/ many new tools for modern web technology, and all
contract-register or download as Zip of them are applicable to us to work in SharePoint
3. In a Node command-line, go to the contract- On-Premises and SharePoint Online.
register directory and run
»» > npm install The new tools and web stack work well together, but
»» This installs all the defined packages within there is no pressure to have to learn everything at
the package.json file once. We are learning, and sharing our learnings. We
4. Run Gulp tasks see new pieces and we try to figure out where they
»» > gulp build fit. We hope you will take what we’ve learned and run
»» This will build the project further with it and be successful.
5. Deploy to your SharePoint
»» Update o365-user.js with your account
details and site url
»» > gulp deploy
»» This will build and deploy the JavaScript to
your SharePoint site’s SiteAssets document
library.
6. One-off
»» Create a page with a Content Editor
webpart, and point it to the SiteAssets/
SPApp.html file.

Summary
In summary, we built a modern Angular application
with components using the latest work in PnP
JS Core. We built and pack the source code with
Webpack, and deploy it to SharePoint with Gulp.
© 2017 Progress. All Rights Reserved.

We discuss our choices and describe what makes


this set of tools work together so well, as well as
potential pitfalls and gotchas one might need to be
aware of.

Progress.com 46
About the authors

SharePoint Gurus Bart Bouwhuis


SharePoint Gurus is an award-winning consultancy Bart is a senior SharePoint Consultant at SharePoint
based in Sydney. We specialize in improving Gurus and has focused on client side development
productivity through configuring and developing since MOSS 2007.
Microsoft SharePoint technologies.
He has implemented various tailored solutions for
At SharePoint Gurus, we help companies understand managing training, projects, assets and contracts
what SharePoint is (and often isn’t). Using our deep within SharePoint and currently uses Kendo UI for
knowledge of the product and our years of practical the majority of his projects.
experience, we will help you plan, configure and
implement the right size solution on SharePoint On-
Premises or SharePoint Online (Office 365).

Our goal is to provide unparalleled consulting


services. Our specialist knowledge will speed up
your deployment of SharePoint by helping you
understand how it can be effectively implemented to
achieve your business goals.
© 2017 Progress. All Rights Reserved.

Progress.com 47
John Liu
John is a Senior Consultant based in Sydney
for SharePoint Gurus. He specializes and blogs
frequently on client-side scripting, custom
development, workflows and Forms.

Originally from a technical background in ASP.


NET, he made the jump to focus and work with
numerous SharePoint projects since WSS. John loves
to find ways to bring the latest ASP.NET and web
technologies to the SharePoint world, applying the
latest web developments to extend SharePoint’s
capabilities.

About Progress Worldwide Headquarters

Progress (NASDAQ: PRGS) offers the leading platform for Progress, 14 Oak Park, Bedford, MA 01730 USA
developing and deploying mission-critical business applications. Tel: +1 781 280-4000 Fax: +1 781 280-4095
Progress empowers enterprises and ISVs to build and deliver On the Web at: www.progress.com
cognitive-first applications, that harness big data to derive Find us on facebook.com/progresssw
business insights and competitive advantage. Progress offers twitter.com/progresssw
leading technologies for easily building powerful user interfaces youtube.com/progresssw
across any type of device, a reliable, scalable and secure backend For regional international office locations and contact
platform to deploy modern applications, leading data connectivity information, please go to
to all sources, and award-winning predictive analytics that brings www.progress.com/worldwide
the power of machine learning to any organization. Over 1700
© 2017 Progress. All Rights Reserved.

independent software vendors, 80,000 enterprise customers, and


2 million developers rely on Progress to power their applications.
Learn about Progress at www.progress.com or +1-800-477-6473.

Progress is trademark or registered trademark of Progress Software Corporation and/or one of its subsidiaries or affiliates in the
U.S. and/or other countries. Any other trademarks contained herein are the property of their respective owners.

© 2016 Progress Software Corporation and/or its subsidiaries or affiliates. All rights reserved. Rev 16/09 | 160325-0050

Progress.com 48

You might also like